def __init__(self, observer_catches):
        """
        
        :param observer_catches: Limited use: Used to determine if current catch's Weight Method is 3.
        """
        super().__init__()
        self._logger = logging.getLogger(__name__)

        self._observer_catches = observer_catches

        ####
        # Set up Table CATCH_ADDITIONAL_BASKETS for use with Weight Method 3 catch weight calculations
        ####

        # Until DB Sync is extended to include CATCHES_ADDITIONAL_BASKETS, it's possible this table
        # may not exist. Create it if it's missing
        if not CatchAdditionalBaskets.table_exists():
            CatchAdditionalBaskets.create_table()
            self._logger.info("Observer DB table CatchAdditionalBaskets doesn't exist. Created.")
        else:
            n_records = CatchAdditionalBaskets.select().count()
            self._logger.info(f"Table CatchAdditionalBaskets exists with {n_records} records.")

        self._catch_additional_baskets_view_model = CatchAdditionalBasketsViewModel(
                sort_role='catch_addtl_baskets', sort_reverse=True)

        # For convenience, build a dictionary of CAB_BASKET_TYPEs extracted from LOOKUPS Table.
        self._catch_additional_basket_types = self._get_catch_additional_basket_types_from_lookup_table()

        # Get signal that current catch has changed
        self._observer_catches.catchIdChanged.connect(self._handle_change_in_current_catch)
 def _get_basket_db_item(self, basket_id):
     if self._is_mixed_species():
         basket_q = CatchAdditionalBaskets.get(
             CatchAdditionalBaskets.catch_addtl_baskets == basket_id)
     else:
         basket_q = SpeciesCompositionBaskets.get(
             SpeciesCompositionBaskets.species_comp_basket == basket_id)
     return basket_q
    def hasWM3BasketData(self):
        """
        :return: True if any weighed or unweighed data for the current catch in Table CATCH_ADDITIONAL_BASKETS.
        """
        if self._current_catch is None:
            self._logger.debug("No catch, no catch additional baskets")
            return False

        try:
            basket_q = CatchAdditionalBaskets.select().where(CatchAdditionalBaskets.catch == self._current_catch.catch)
            self._logger.info(f"Found {basket_q.count()} weighed or unweighed (tallied) catch additional baskets " +
                              f"for Catch ID {self._current_catch.catch}.")
            return basket_q.count() > 0
        except CatchAdditionalBaskets.DoesNotExist:
            self._logger.debug("No catch additional baskets found for Catch ID {self._current_catch.catch}.")
            return False
    def _add_additional_basket(self, basket_weight, basket_type):
        """
        Utility for adding weighed and unweighed baskets to CATCH_ADDITIONAL_BASKETS
        for use in WM3 calculation of catch weight.
        
        :param basket_weight: 
        :param basket_type: digit as text.
        :return: 
        """
        if basket_type not in self._catch_additional_basket_types:
            raise Exception(f"Unrecognized catch additional basket type {basket_type}")

        if self._weight_method != '3':
            msg = f"Weight Method is '{self._weight_method}', not '3'; additional baskets not allowed."
            self._logger.error(msg)
            raise Exception(msg)

        # Consistency check: if basket type is unweighed, basket weight should be 0 or None
        if basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_UNWEIGHED_FULL and \
                basket_weight is not None and basket_weight != 0.0:
            msg = f"Basket type unweighed should have no or zero basket weight."
            self._logger.error(f"Basket type unweighed should have no or zero basket weight.")
            raise Exception(msg)

        basket_type_description = self._catch_additional_basket_types[basket_type]
        self._logger.debug(f'Add catch additional basket with wt={basket_weight} and '
                           f'type={basket_type_description}.')

        if basket_weight is None:
            basket_weight = 0.0
            self._logger.debug(f"Unweighted baskets will be given weight of 0.0")

        new_basket = None
        try:
            new_basket = CatchAdditionalBaskets.create(
                    catch=self._current_catch.catch,
                    basket_weight=float(basket_weight),
                    basket_type=basket_type,
                    created_by=ObserverDBUtil.get_current_user_id(),
                    created_date=ObserverDBUtil.get_arrow_datestr(date_format=ObserverDBUtil.oracle_date_format)
            )
        except Exception as e:
            self._logger.error(e)
        finally:
            return new_basket
 def countOfUnweighedCatchAdditionalBaskets(self):
     """
     :return: The number of unweighed catch additional baskets for the current catch.
     """
     if self._current_catch is None:
         self._logger.debug("No catch, no catch additional baskets")
         return 0
     self._logger.debug(f"Current catch ID is {self._current_catch.catch}.")
     try:
         basket_q = CatchAdditionalBaskets.select().where(
                 (CatchAdditionalBaskets.catch == self._current_catch.catch) &
                 (CatchAdditionalBaskets.basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_UNWEIGHED_FULL))
         self._logger.info(f"Found {basket_q.count()} unweighed (tallied) catch additional baskets " +
                           f"for Catch ID {self._current_catch.catch}.")
         return basket_q.count()
     except CatchAdditionalBaskets.DoesNotExist:
         self._logger.debug("No unweighed catch additional baskets found for Catch ID {self._current_catch.catch.")
         return 0
 def remove_an_additional_unweighed_full_basket(self):
     """
     Use in Weight Method 3 data entry to reduce the tally of unweighed baskets by one.
     
     Remove one unweighed catch additional basket. Since the only purpose of these entries is to maintain
     a tally of unweighed baskets, and there's no data of interest other than the basket_type being unweighed,
     the implementation may remove any unweighed entry.
     
     Should not be called if no unweighed baskets are present, but in that case doesn't throw exception -
     just logs error message and returns.
     :return: 
     """
     try:
         # Get the first (earliest?) record for this catch with an unweighed basket type.
         basket_q = CatchAdditionalBaskets.get(
                 (CatchAdditionalBaskets.catch == self._current_catch.catch) &
                 (CatchAdditionalBaskets.basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_UNWEIGHED_FULL))
         basket_q.delete_instance()
         self._logger.debug(
                 f"WM3: Deleted an unweighed catch additional basket for Catch ID {self._current_catch.catch}.")
     except CatchAdditionalBaskets.DoesNotExist:
         self._logger.error("WM3: No unweighed baskets to remove.")
Ejemplo n.º 7
0
    def _add_catch_basket(self, weight):
        """
        Used only for special-case species MIX, a pseudo-species whose use indicates the basket data
        should be added to catch-level bucket, CATCH_ADDITIONAL_BASKETS rather than to a species-specific
        bucket, SPECIES_COMP_BASKETS.

        Add basket item to DB and model.

        Note: CATCH_ADDITIONAL_BASKETS does not have a field for count.

        @param weight: lbs
        """
        self._logger.debug(f'Add catch (not species) basket. wt: {weight}')
        # Get the current catch and exit if not defined
        current_catch_id = self._current_species_comp_item.species_composition.catch.catch
        if current_catch_id is None:
            self._logger.error('Catch ID is None')
            return

        if weight is None:
            weight = 0.0

        try:
            new_basket = CatchAdditionalBaskets.create(
                catch=current_catch_id,
                basket_weight=weight,
                created_by=ObserverDBUtil.get_current_user_id(),
                # FIELD-2087: Using default dateformat for time display; reformat before sync later
                created_date=ObserverDBUtil.get_arrow_datestr(
                    date_format=ObserverDBUtil.default_dateformat),
            )
            self._baskets_model.add_basket(new_basket)
            self._logger.info(f'Added addl basket wt: {weight}')

        finally:
            self._calculate_totals()
            self.basketAdded.emit()
    def remove_additional_basket(self, basket_id):
        """
        While this method can be used for either weighed or unweighed (tally) baskets, it's intended for use
        with weighed baskets. With unweighed baskets, any entry can be removed - it doesn't matter which -
        so it's easier to use remove_an_additional_unweighed_full_basket() (no basket id needed).
        
        :param basket_id: 
        :return: None
        """
        if not basket_id:
            self._logger.error(f"Passed a null id for CatchAdditionalBaskets. Taking no action.")
            return

        if self._weight_method != '3':
            msg = f"Weight Method is '{self._weight_method}', not '3'; removing additional baskets not allowed."
            self._logger.error(msg)
            raise Exception(msg)

        try:
            basket_q = CatchAdditionalBaskets.get(CatchAdditionalBaskets.catch_addtl_baskets == basket_id)
            basket_q.delete_instance()
            self._logger.debug(f"Deleted catch additional basket ID {basket_id} from database.")

            # Remove from view model - if a weighed basket. Unweighed baskets aren't added to view,
            # so won't be found here (i.e. idx < 0).
            idx = self._catch_additional_baskets_view_model.get_item_index('catch_addtl_baskets', basket_id)
            if idx >= 0:
                self._catch_additional_baskets_view_model.remove(idx)
                self._logger.debug(f"Deleted catch additional weighed basket ID {basket_id} from view model.")
            else:
                if basket_q.basket_type in (ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_PARTIAL,
                                            ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_FULL):
                    self._logger.error(f"Error looking up catch additional basket ID {basket_id} in view model")
            return
        except CatchAdditionalBaskets.DoesNotExist as e:
            self._logger.error(e)
            return
    def get_WM3_catch_values(self) -> Dict[str, QVariant]:
        """
        Get all the Weight Method 3 information needed to display catch weight and its components.
        
        :return: Values are returned as float or int, not text.
        """
        if self._current_catch is None:
            return {}

        n_weighed_full_baskets = 0
        weighed_full_total_weight = 0.0
        n_weighed_partial_baskets = 0
        weighed_partial_total_weight = 0.0
        n_unweighed_baskets = 0

        baskets_q = CatchAdditionalBaskets.select().where(CatchAdditionalBaskets.catch == self._current_catch.catch)
        for basket in baskets_q:
            if basket.basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_UNWEIGHED_FULL:
                n_unweighed_baskets += 1
            elif basket.basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_PARTIAL:
                n_weighed_partial_baskets += 1
                weighed_partial_total_weight += basket.basket_weight
            elif basket.basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_FULL:
                n_weighed_full_baskets += 1
                weighed_full_total_weight += basket.basket_weight
            else:
                self._logger.error(f"Unrecognized catch_type {basket.basket_type}. Ignored.")

        wm3_dict = {
            'N_WEIGHED_FULL_BASKETS': n_weighed_full_baskets,
            'WEIGHED_FULL_TOTAL_WEIGHT': weighed_full_total_weight,
            'N_WEIGHED_PARTIAL_BASKETS': n_weighed_partial_baskets,
            'WEIGHED_PARTIAL_TOTAL_WEIGHT': weighed_partial_total_weight,
            'N_UNWEIGHED_BASKETS': n_unweighed_baskets
        }
        return wm3_dict
    def _edit_additional_basket(self, basket_id, basket_weight: float, basket_type: int=None):
        """
        Change one or more non-null fields of an entry in CATCH_ADDITIONAL_BASKETS.
        Since all fields whose change is supported are non-nullable, a value of None indicates:
        don't change field.
        Changes to fields created_date and created_by are not supported - no need as yet.
        
        :param basket_id: Primary key to CATCH_ADDITIONAL_BASKETS
        :param basket_weight: as a float
        :param basket_type: as an integer
        :return: 
        """
        if not basket_id:
            self._logger.error(f"Passed a null id for CatchAdditionalBaskets. Taking no action.")
            return

        if basket_weight is None and basket_type is None:
            self._logger.info(f"Neither basket_weight nor basket_type specified. Taking no action.")
            return

        if self._weight_method != '3':
            msg = f"Weight Method is '{self._weight_method}', not '3';" \
                  f"editing additional baskets not allowed."
            self._logger.error(msg)
            raise Exception(msg)

        # Consistency check: if requested basket type is unweighed,
        # requested basket weight should be 0 or None
        if basket_type == ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_UNWEIGHED_FULL and \
                basket_weight is not None and basket_weight != 0.0:
            msg = f"Basket type unweighed should have no or zero basket weight."
            self._logger.error(f"Basket type unweighed should have no or zero basket weight.")
            raise Exception(msg)

        try:
            basket_q = CatchAdditionalBaskets.get(CatchAdditionalBaskets.catch_addtl_baskets == basket_id)
            weight_modified_msg = ""
            type_modified_msg = ""
            if basket_weight is not None:
                basket_q.basket_weight = basket_weight
                weight_modified_msg = f"Weight={basket_weight}"
            if basket_type is not None:
                # Note: Changing a basket from unweighed to weighed or vice versa is not allowed.
                basket_q.basket_type = basket_type  # DB type is integer
                type_modified_msg = f"Basket Type={basket_type}"
            basket_q.save()
            self._logger.debug(f"Updates to catch additional basket ID {basket_id}: " +
                               weight_modified_msg + " " + type_modified_msg)

            # Update view model containing weighed baskets (full or partial)
            if basket_q.basket_type in (ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_FULL,
                                        ObserverCatchBaskets.LOOKUP_VALUE_CAB_BASKET_TYPE_WEIGHED_PARTIAL):
                idx = self._catch_additional_baskets_view_model.get_item_index('catch_addtl_baskets',
                                                                               basket_id)
                if idx >= 0:
                    if basket_weight is not None:
                        self._catch_additional_baskets_view_model.setProperty(
                                idx, 'basket_weight', basket_weight)
                        self._logger.debug(f"Basket ID {basket_id}'s view model weight={basket_weight}.")
                    if basket_type is not None:
                        self._catch_additional_baskets_view_model.setProperty(
                                idx, 'basket_type', basket_type)
                        self._logger.debug(f"Basket ID {basket_id}'s view model type="
                                           f"{self._catch_additional_basket_types[basket_type]}.")

        except CatchAdditionalBaskets.DoesNotExist as e:
            self._logger.error(e)