Source code for tendril.inventory.electronics

# Copyright (C) 2015 Chintalagiri Shashank
#
# This file is part of Tendril.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
Electronics Inventory module documentation (:mod:`inventory.electronics`)
=========================================================================
"""

import os
import csv

from tendril.utils.config import ELECTRONICS_INVENTORY_DATA
from tendril.utils.types.lengths import Length
from tendril.conventions.electronics import fpiswire_ident
from tendril.entityhub.transforms import ContextualReprNotRecognized

import acquire
from db.controller import get_inventorylocationcode

from tendril.utils import log
logger = log.get_logger(__name__, log.INFO)

inventory_locations = []


[docs]class InventoryLine(object): def __init__(self, ident, qty=None, parent=None): self._ident = ident self._qty = qty self._parent = parent self._reservations = [] @property def ident(self): return self._ident @property def avail_qty(self): try: if fpiswire_ident(self._ident): return Length(str(self._qty) + 'm') - self.reserved_qty else: return self._qty - self.reserved_qty except AttributeError: return self._qty - self.reserved_qty @property def reserved_qty(self): reserved = 0 for reservation in self._reservations: reserved += reservation[0] return reserved
[docs] def reserve_qty(self, value, earmark): try: if fpiswire_ident(self._ident) and not isinstance(value, Length): value = Length(str(value) + 'm') except AttributeError: pass if value > self.avail_qty: raise ValueError logger.debug("Reserving " + self.ident + " in " + self._parent._dname + " for " + earmark + " : " + str(value)) self._reservations.append((value, earmark))
@property def earmarks(self): em = [] for reservation in self._reservations: em.append(reservation[1]) return em
[docs] def _reservation_gen(self): for reservation in self._reservations: yield reservation
[docs] def get_reservation_gen(self): return self._reservation_gen()
def __repr__(self): return self.ident + '\t' + str(self._qty) + '\t' + \ str(self.reserved_qty)
[docs]class InventoryLocation(object): def __init__(self, name, dname, reader): self._name = name self._dname = dname self._lines = [] self._code = None self._get_code() self._reader = reader if reader is not None: self._load_from_reader()
[docs] def _get_code(self): self._code = get_inventorylocationcode(self._dname)
@property def name(self): return self._dname @property def tf(self): return self._reader.tf
[docs] def _load_from_reader(self): try: for (ident, qty) in self._reader.tf_row_gen: self._lines.append(InventoryLine(ident, qty, self)) except ContextualReprNotRecognized: logger.error("Inventory has unrecognized components.") raise ContextualReprNotRecognized
[docs] def get_ident_qty(self, ident): avail_qty = 0 is_here = False for line in self._lines: if line.ident == ident: is_here = True avail_qty += line.avail_qty if is_here: logger.debug("Found " + ident + " in " + self._dname + " : " + str(avail_qty)) # if fpiswire_ident(ident): # avail_qty = Length(str(avail_qty)+'m') return avail_qty else: return None
[docs] def get_reserve_qty(self, ident): reserve_qty = 0 is_here = False for line in self._lines: if line.ident == ident: is_here = True reserve_qty += line.reserved_qty if is_here: return reserve_qty else: return None
[docs] def reserve_ident_qty(self, ident, qty, earmark): for line in self._lines: if line.ident == ident: if line.avail_qty > qty: line.reserve_qty(qty, earmark) return 0 elif line.avail_qty > 0: qty = qty - line.avail_qty line.reserve_qty(line.avail_qty, earmark) if qty > 0: raise ValueError("Unexpected Overrequisition : " + ident) return qty
@property def earmarks(self): earmarks = [] for line in self._lines: for em in line.earmarks: if em not in earmarks: earmarks.append(em) return earmarks
[docs] def _reservation_gen(self): for line in self._lines: if line.reserved_qty > 0: yield (line.ident, line.get_reservation_gen())
[docs] def get_reservation_gen(self): return self._reservation_gen()
[docs] def commit_reservations(self): pass
@property def lines(self): return self._lines
[docs]def init_inventory_locations(regen=True): for idx, item in enumerate(ELECTRONICS_INVENTORY_DATA): logger.info("Acquiring Inventory Location : " + item['location']) retries = 1 while retries: try: reader = acquire.get_reader(idx) inventory_locations.append( InventoryLocation(item['sname'], item['location'], reader) ) break except ContextualReprNotRecognized: if not regen: raise retries -= 1 logger.warning( "Regenerating Transform for Inventory " "Location {0}.".format(idx) ) logger.warning( "All inventory functions will be unreliable " "until the transform is manually verified." ) acquire.gen_canonical_transform(idx)
[docs]def get_total_availability(ident): total_avail = 0 for location in inventory_locations: lqty = location.get_ident_qty(ident) if lqty is not None: total_avail += lqty return total_avail
[docs]def get_total_reservations(ident): total_reserve = 0 for location in inventory_locations: lqty = location.get_reserve_qty(ident) if lqty is not None: total_reserve += lqty return total_reserve
[docs]def reserve_items(ident, qty, earmark, die_if_not=True): if qty <= 0: raise ValueError if fpiswire_ident(ident) and not isinstance(qty, Length): qty = Length(str(qty) + 'm') for location in inventory_locations: lqty = location.get_ident_qty(ident) if lqty is not None: if lqty > qty: location.reserve_ident_qty(ident, qty, earmark) return 0 elif lqty > 0: location.reserve_ident_qty(ident, lqty, earmark) qty -= lqty if qty == 0: return 0 if qty > 0: logger.warning('Partial Reservation of ' + ident + ' for ' + earmark + ' : Short by ' + str(qty)) if die_if_not is True: raise ValueError("Insufficient Qty. " "Call with die_if_not=True if handled downstream" ) return qty
[docs]def export_reservations(folderpath): earmarks = [] for location in inventory_locations: for em in location.earmarks: if em not in earmarks: earmarks.append(em) for location in inventory_locations: dump_path = os.path.join(folderpath, 'reserve-' + location._dname + '.csv') with open(dump_path, 'wb') as f: w = csv.writer(f) header = ['Ident'] + earmarks + ['Total', 'Remaining'] w.writerow(header) for ident, emgen in location.get_reservation_gen(): row = [ident] + [0] * (len(earmarks) + 2) total = 0 for reservation in emgen: row[earmarks.index(reservation[1]) + 1] += reservation[0] total += reservation[0] for idx, hdr in enumerate(earmarks): if row[idx + 1] == 0: row[idx + 1] = '' row[header.index('Total')] = total row[header.index('Remaining')] = get_total_availability(ident) w.writerow(row) logger.info("Exported " + location._dname + " Reservations to File : " + dump_path)
init_inventory_locations()
[docs]def get_inventory_location(idx): for loc in inventory_locations: if loc._code == int(idx): return loc raise ValueError
[docs]def get_recognized_repr(regen=False): global recognized_representations if regen or recognized_representations is None: rval = [] for loc in inventory_locations: rval.extend(list(loc.tf.names)) recognized_representations = rval return set(recognized_representations)
recognized_representations = None get_recognized_repr()
[docs]def get_inventory_stage(ident): inv_loc_status = {} inv_loc_transform = {} for loc in inventory_locations: qty = loc.get_ident_qty(ident) or 0 reserve = loc.get_reserve_qty(ident) or 0 inv_loc_status[loc._code] = (loc.name, qty, reserve, qty - reserve) inv_loc_transform[loc._code] = (loc.name, loc.tf.get_contextual_repr(ident)) inv_total_reservations = get_total_reservations(ident) inv_total_quantity = get_total_availability(ident) inv_total_availability = inv_total_quantity - inv_total_reservations from tendril.inventory.guidelines import electronics_qty inv_guideline = electronics_qty.get_guideline(ident) from tendril.entityhub.guidelines import QtyGuidelineTableRow inv_guideline = QtyGuidelineTableRow(ident, inv_guideline) inv_stage = { 'loc_status': inv_loc_status, 'total_reservations': inv_total_reservations, 'total_quantity': inv_total_quantity, 'total_availability': inv_total_availability, } return {'inv_status': inv_stage, 'inv_transform': inv_loc_transform, 'inv_guideline': inv_guideline}