extools.view¶
ExView is a fully functional wrapper around the Extender View object
that raises exceptions instead of providing non-zero returns on error
for the methods defined in extools.view.ExView.WRAP
.
It supports all the methods of the Extender.View
class, along with many
extra helpers.
On startup an ExView
introspects the underlying Sage view to automatically
determine:
- The view’s composition tree
- The field names
- The allowed indexes and their key fields
Based on this information, the class automatically configures itself to:
- Self-compose on request (see
extools.view.ExView.compose()
) - Validate orders and keys before errors are raised by Sage
- Automatically add the correct helpers
- For detail views, the
.lines()
,.lines_from(start, end)
,.lines_where(key=value, key=value, ...)
generators andnewline()
helper. - For optional field views, or any view composed with an optional
field view, enable the
create_optfield
,update_optfield
,get_optfield
,update_or_create_optfield
,seek_to_optfield
, anddelete_optfield
helpers.
- For detail views, the
-
extools.view.
exview
(rotoid, index=-1, seek_to={}, fetch=True, compose=False)[source]¶ Context manager to cleanly open and use an ExView.
Parameters: - rotoid (str) – the RotoID of the Sage view.
- index (int) – the index to open the view with.
- seek_to (dict|None) – field value mapping to seek to after opening. When set
to an empty dictionary, seek to the first line in the view. If set
to
None
, disable seek after opening. - fetch (bool) – automatically fetch the first matched record?
- compose (bool) – automatically compose before seeking?
Raises: ExViewError
Return type: None
When called the context manager will yield an open view object. On exit of the block the view will be closed cleanly.
with exview("EX0001") as view: try: view.recordClear() view.browse("") view.fetch() value = view.get("KEY") except ExViewError as err: showMessageBox("Failed to get KEY, {}.".format(err))
-
extools.view.
exgen
(rotoid, index=-1, seek_to={})[source]¶ Generator for iterating over all the entries in a view.
Parameters: - rotoid (str) – the RotoID of the Sage view.
- index (int) – the index to open the view with.
- seek_to (dict|None) – field value mapping to seek to after opening. When set
to an empty dictionary, seek to the first line in the view. If set
to
None
, disable seek after opening.
Raises: ExViewError
Return type: None
When called, the generator will seek the view to the requested records, or the first record if
seek_to
is empty. It will then yield all matching rows and then cleanly close the view.for record in exgen("EX0001"): try: record.get("FIELD") except ExViewError as err: showMessageBox("Failed to get FIELD, {}.".format(err))
-
extools.view.
EXVIEW_BLACKLIST
= {'OE0999'}¶ Views that can never be composed with any other.
-
class
extools.view.
ExView
(rotoid, index=-1, seek_to={}, native_types=False, fetch=True, _root=True, _me=None, _cviews=[])[source]¶ An exception raising wrapper around the Extender View class.
ExViews can be used to replace repetitive error checking and to take advantage of the try/except/else/finally construct in Python.
Parameters: - rotoid (str.) – the RotoID of the Sage view.
- index (int.) – the index to open the view with.
- seek_to (dict|None) – field value mapping to seek to after opening. When set
to an empty dictionary, seek to the first line in the view. If set
to
None
, disable seek after opening.
Raises: ExViewError
Return type: Replace this:
view = openView("EX0001") if not view: showMessageBox("Failed to open view.") return 1 rc = view.recordClear() if rc != 0: showMessageBox("Failed to record clear.") return 1 br = view.browse("") if br != 0: showMessageBox("Failed to browse.") return 1 fe = view.fetch() if fe != 0: showMessageBox("Failed to fetch.") return 1 value = view.get("KEY") if view: view.close()
With this:
try: view = ExView("EX0001") value = view.get("KEY") except ExViewError as err: showMessageBox("Failed to get KEY, {}.".format(err)) return 1 finally: view.close()
You can even include the traceback using the ExMessages:
try: view = ExView("EX0001") value = view.get("KEY") except ExViewError as err: # Use ExMessages to display an error level message box and # log to a file (if configured). The last exception traceback # will be included in both the box and log if ``exc_info=True``. exm.error("Failed to get KEY, {}.".format(err), exc_info=True) return 1 finally: view.close()
ExViews can also self-compose, composing the view and all its related views automatically. Fully composed views require more database operations every time the header is changed and do not perform as well as standalone views or SQL access. However, in cases where performance isn’t paramount you cannot beat the convenience.
from extools import success from extools.view import ExView from extools.message import ExMessages exm = ExMessages("compose-test", ExMessages.DEBUG) try: exv = ExView("OE0520") exv.compose() except Exception as e: exm.error("Failed to setup view: {}".format(e), exc_info=True) # Seek to order ORD000000000064 try: # Use index 1, key (ORDNUMBER, ) exv.order(1) exv.seek_to(ORDNUMBER="ORD000000000064") except Exception as e: exm.error("Failed to seek: {}".format(e), exc_info=True) # Perform an action on each of the detail lines in the order try: for line in exv.oe0500.lines(): exm.info("Read new line {}".format(line.get("ITEM"))) # perform many important actions... except Exception as e: exm.error("Failed to perform action: {}".format(e), exc_info=True)
-
lines
(self)¶ A generator that yields all lines in a detail view.
Only available on detail views.
Return type: None Yields: ExView for line in oe500.lines(): # line now contains the oe0500 view seeked to the next line
-
lines_from
(start, end=None)¶ A generator that yields all lines from
start
toend
.Only available on detail views.
Parameters: - start (int) – line to start at (numbering starts at 0)
- end (int) – line to end on (inclusive)
Yields: ExView
Return type: None
for line in oe500.lines_from(2, 3): # line now contains the oe0500 view seeked to the second line # there will be one more iteration with the third line
-
lines_where
(key=value, key=value, key=value, ...)¶ A generator that yields all lines matched by browsing for the provided keys. All key value pairs are combined using the
AND
condition and used to browse.Only available on detail views.
Parameters: - key (str) – a field name in the view.
- value (any) – the value to browse to
Yields: ExView
Return type: None
for line in oe500.lines_where(ORDUNIQ=234234): # line now contains the oe0500 view seeked to first line or # the oder with unqiue key 234234.
-
create_optfield
(field, value)¶ Create a new optional field, set its value, and save it.
Parameters: - field (str) – Optional field name.
- value (builtins.*) – Optional field value.
Return type: None
Raises: ExViewError
try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. oe0500.create_optfield("MYFIELD", "NEWVAL") except ExViewError as e: # Do something on fail.
-
update_optfield
(field, value)¶ Update an existing optional field.
Parameters: - field (str) – Optional field name.
- value (builtins.*) – Optional field value.
Return type: None
Raises: ExViewError
try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. oe0500.update_optfield("MYFIELD", "UPDATEDVAL") # The composed OE0522 view is also accessible. oe0500.oe0522.update_optfield("MYFIELD", "UP2DATEVAL") except ExViewError as e: # Do something on fail.
-
delete_optfield
(field)¶ Delete an existing optional field.
Parameters: field (str) – Optional field name. Return type: None Raises: ExViewError try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. oe0500.delete_optfield("MYFIELD") # The composed OE0522 view is also accessible. oe0500.oe0522.delete_optfield("MYFIELD") except ExViewError as e: # Do something on fail.
-
get_optfield
(field)¶ Get the value of an existing optional field.
Parameters: field (str) – Optional field name. Returns: Optional field value. Return type: builtins.* Raises: ExViewError try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. value = oe0500.get_optfield("MYFIELD") # The composed OE0522 view is also accessible. value = oe0500.oe0522.get_optfield("MYFIELD") except ExViewError as e: # Do something on fail.
-
seek_to_optfield
(field)¶ Seek the view to an existing optional field.
Parameters: field (str) – Optional field name. Return type: None Raises: ExViewError try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. oe0500.seek_to_optfield("MYFIELD") # The composed OE0522 view is also accessible. # Now that is has seeked to MYFIELD extract the value with get. value = oe0500.oe0522.get("VALUE") except ExViewError as e: # Do something on fail.
-
update_or_create_optfield
(field, value)¶ Update an existing optional field if it exists, otherwise create it.
Parameters: - field (str) – Optional field name.
- value (builtins.*) – Optional field value.
Return type: None
Raises: ExViewError
try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. oe0500.update_or_create_optfield("MYFIELD", "UPDATEDVAL") # The composed OE0522 view is also accessible. oe0500.oe0522.update_or_create_optfield("MYFIELD", "UPDATEVAL") except ExViewError as e: # Do something on fail.
-
has_optfield
(field)¶ Check if an optional field exists.
Parameters: field (str) – Optional field name. Returns: True if an optional field with name field
exists.Return type: bool Raises: ExViewError try: oe0500 = ExView("OE0500") oe0500.compose() # When the view is composed, the associated optional field view # OE0522 is auto-detected so you can call the ``*_optfield`` # methods directly on oe0500. if oe0500.has_optfield("MYFIELD"): showMessageBox("MYFIELD already defined.") # The composed OE0522 view is also accessible. oe0500.oe0522.has_optfield("MYFIELD", "UPDATEVAL") except ExViewError as e: # Do something on fail.
-
optfields
¶ Get all optional fields for a view.
Returns: mapping of field names to values. Return type: dict Raises: ExViewError try: oe0500 = ExView("OE0500") oe0500.compose() # oe0500.optfields is now poplated with all the optional # fields currently defined for the header. # { "FIELDNAME": "VALUE", "F1": 1, "MYFIELD": "VALUE"} if "MYFIELD" in oe0500.optfields.keys(): showMessage("MYFIELD is set to {}".format( oe0500.optfields["MYFIELD"])) except ExViewError as e: # Do something on fail.
-
ATTRS
= {'A': 2, 'C': 16, 'E': 4, 'K': 8, 'P': 32, 'R': 48, 'X': 64}¶
-
ATTR_A
= 2¶
-
ATTR_COMPUTED
= 16¶
-
ATTR_EDITABLE
= 4¶
-
ATTR_KEY
= 8¶
-
ATTR_P
= 32¶
-
ATTR_R
= 48¶
-
ATTR_X
= 64¶
-
DETAIL_VIEW_HINTS
= {'CNTENTR', 'DETAILNUM', 'ENTRY', 'LINENUM'}¶ Views containing any one of these fields may be detail views.
-
OPTFIELD_VIEW_HINTS
= {'OPTFIELD'}¶ Views containing any one of these fields may be optional field views.
-
WRAP
= ['fetchLock', 'readLock', 'insert', 'delete', 'init', 'post', 'process', 'verify', 'recordClear', 'dirty', 'unlock', 'cancel', 'recordGenerate', 'put', 'browse']¶ These View functions raise an
ExViewError
on non-zero return.
-
all
(ascending=True)[source]¶ Generator that yields once for each record in the view.
Raises: ExViewError Yields: ExView
-
copy_to
(view2, force=True, exclude=[], include=[], post_process=[], skip_keys=True, skip_computed=True, save=False)[source]¶ Copy the current object to view2.
Parameters: - view2 (ExView) – the view to copy to.
- exclude (str[]) – Fields to exclude from copy.
- include (str[]) – Fields to include, excluding all others.
- post_process (int[]) – run process with these processcmds after copy.
- skip_keys (bool) – skip fields with the Key attribute. Default: yes.
- skip_computed (bool) – skip fields with the Key attribute. Default: yes.
- save (bool) – insert the object after copy. Default: no.
Raises: ExViewError – when any error occurs during the copy.
-
create
(**fields)[source]¶ Generate and insert a new entry with field/value pairs.
Parameters: fields (field=value) – field value pairs that will be set on the new entry. Return type: None Raises: ExViewError
-
current_key
()[source]¶ Get the current unique key identifying the view record.
Returns: {field: value, field: value…}
-
exists
()[source]¶ Wrap exists to return True or False and not raise.
Returns: True if record in view exists (has been added), else False Return type: bool
-
fetch
()[source]¶ A special wrapper because a non-zero fetch return isn’t an error.
Returns: True if a new line was fetched, else False. Return type: bool
-
get
(field, _type=-1, size=-1, precision=-1, verify=True)[source]¶ A special wrapper because get doesn’t return 0 on success.
Parameters: - field (str) – field name to get.
- verify – verify that the field is listed in the view fields?
Type: bool
Returns: value in the view.
Return type: builtin.*
Raises: ExViewFieldDoesNotExist
-
has_optfield_view
¶ Is this view composed with an optional field view?
Returns: True if this view is composed with an optional field view. Return type: bool
-
is_optfield_view
¶ Is this an optional field view?
Returns: True if this view is an optional field view. Return type: bool
-
order
(_ord)[source]¶ Wrap the order to track state in the class as it can’t be queried.
Parameters: index (int) – the index ID to order by. Return type: None Raises: ExViewError
-
parent_key
()[source]¶ Get the current unique key identifying the view record’s parent.
Only relevant for detail views, return the key components before the last one.
The views, as classified by Sage, may either be header, detail, flat or batch. Both detail, and headers with composite keys, may be enumerated.
Returns: {field: value, field: value…}
-
read
()[source]¶ A special wrapper to raise ExViewRecordDoesNotExist.
Raises: ExViewRecordDoesNotExist
-
seek_to
(fetch=True, **kwargs)[source]¶ Intelligently seek to a specific entry.
This seek to implementation accepts an arbitrary set of field value pairs and then seeks to the entry using one of three methods:
- If the current View order index is made up of exactly the fields requested, perform a straight put and read.
- If the current View has an index made up of exactly the fields requested, temporarily change the index and perform and put a read.
- If the current View does not have and index made up of exactly the fields requested, attempt to browse and fetch the record.
Parameters: - fetch (bool) – fetch after seeking? Default to true.
- kwargs (dict) – (key)=(value) pairs, where the keys must be the same as the current index keys.
Return type: None
Raises: ExViewError
viewid = "OE0500" try: exv = ExView(viewid) # Open Order Details, default view order 0 # Seek to the 7th line of the order with unique key 1024 # The default view order is 0: (ORDUNIQ, LINENUM, ) exv.seek_to(ORDUNIQ=1024, LINENUM=7) # Get details from the record and process or update. item = exv.get("ITEM") ... except ExViewError as e: # The error, "failed to [open|seek]", is contained in the # error message. showMessage("Error doing something with view {}: {}".format( viewid, e))
-
to_dict
()[source]¶ Return all the fields in a view as a dictionary.
Useful for caching full rows for later use.