def search(text, params): """Return the search results from a long enough text. To avoid unnecessary search, text length must be at least 2 chars. The method searches into medicines, equipment, laboratory and telemedical sets. The results are sorted by "parent" (Molecule/Equipment) and contains required quantities for each set if any, current quantity, group (MoleculeGroup or EquipmentGroup) and other names previously encountered (in Medicine and Article records). :param str text: String to search in the different database models. :param object params: Global Parameters of the application \ (:class:`pharmaship.gui.view.GlobalParameters`) :return: List of parsed results. See ``pharmaship/schemas/search.json`` \ for details. :rtype: list """ if len(text) < 2: return [] result = [] result += get_molecules(text, params) result += get_equipments(text, params) query_count_all() return result
def response_use(self, source, dialog, article, builder): # Get response quantity_obj = builder.get_object("quantity") quantity = quantity_obj.get_value() if quantity < 0: return False if quantity == 0: # Nothing to do dialog.destroy() return # Get the object article_obj = models.Article.objects.get(id=article['id']) if article["quantity"] == quantity: article_obj.used = True article_obj.save() models.QtyTransaction.objects.create(transaction_type=2, value=quantity, content_object=article_obj) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def dialog_modify(self, source, data): builder = Gtk.Builder.new_from_file(utils.get_template("location_add.glade")) dialog = builder.get_object("dialog") location_combo = builder.get_object("parent") utils.location_combo( location_combo, locations=self.params.locations, active=data["parent"], empty=True, exclude=[data["id"]] ) name = builder.get_object("name") name.set_text(data["sequence"][-1]) btn_modify = builder.get_object("btn_response") btn_modify.set_label(_("Modify this location")) builder.connect_signals({ "on-response": (self.response_modify, data["id"], dialog, builder), "on-cancel": (utils.dialog_destroy, dialog) }) query_count_all() dialog.set_transient_for(self.window) dialog.run() dialog.destroy()
def export_pdf(html_string, filename): """Export the equipment inventory in PDF.""" css_path = os.path.join(settings.PHARMASHIP_REPORTS, filename) try: with open(css_path, "r") as fdesc: css_string = fdesc.read() except IOError as error: log.error("CSS file not readable: %s", error) return None # Create a temporary file tmp_file = tempfile.NamedTemporaryFile(prefix="pharmaship_", suffix=".pdf", delete=False) font_config = FontConfiguration() html = HTML(string=html_string) css = CSS(string=css_string, font_config=font_config) html.write_pdf(target=tmp_file.name, stylesheets=[css], font_config=font_config) query_count_all() return tmp_file.name
def get_telemedical(self): result = { "missing": [], "perished": [], "warning": [], "nc": [] } data = parsers.telemedical.parser(self.params) for element in data: if element["has_nc"]: result["nc"].append(element) if element["required_quantity"] > element["quantity"]: result["missing"].append(element) if element["has_date_expired"]: result["perished"].append(element) continue if element["has_date_warning"]: result["warning"].append(element) continue self.data["telemedical"] = result query_count_all()
def get_molecules(self): result = { "missing": [], "perished": [], "warning": [], "nc": [] } data = parsers.medicines.parser(self.params) for group in data: for element in data[group]: if element["has_nc"]: result["nc"].append(element) if element["required_quantity"] > element["quantity"]: result["missing"].append(element) if element["has_date_expired"]: result["perished"].append(element) continue if element["has_date_warning"]: result["warning"].append(element) continue self.data["molecules"] = result query_count_all()
def get_first_aid_kit(self): result = { "missing": [], "perished": [], "warning": [], "nc": [] } data = parsers.first_aid.parser(self.params) for kit in data: for element in kit["elements"]: if element["has_nc"]: result["nc"].append(element) if element["required_quantity"] > element["quantity"]: result["missing"].append(element) if element["has_date_expired"]: result["perished"].append(element) continue if element["has_date_warning"]: result["warning"].append(element) continue self.data["first_aid_kit"] = result query_count_all()
def response_modify(self, source, location_id, dialog, builder): fields = { "entry": [ "name" ], "combobox": [ "parent" ] } cleaned_data = utils.get_form_data(LocationForm, builder, fields) if cleaned_data is None: return if cleaned_data["parent_id"] == 0: cleaned_data["parent_id"] = None location = Location.objects.get(id=location_id) location.name = cleaned_data["name"] location.parent_id = cleaned_data["parent_id"] location.save() dialog.destroy() self.refresh_grid() query_count_all()
def response_add(self, source, dialog, builder): fields = { "entry": [ "name" ], "combobox": [ "parent" ] } cleaned_data = utils.get_form_data(LocationForm, builder, fields) if cleaned_data is None: return if cleaned_data["parent_id"] == 0: cleaned_data["parent_id"] = None Location.objects.create( parent_id=cleaned_data["parent_id"], name=cleaned_data["name"] ) dialog.destroy() self.refresh_grid() query_count_all()
def dialog_add(self, source, item): builder = Gtk.Builder.new_from_file(utils.get_template("subitem_add.glade")) dialog = builder.get_object("dialog") combobox = builder.get_object("item") self.subitem_combobox(combobox, item) combobox.connect("changed", self.combobox_changed, builder) remaining = builder.get_object("remaining") quantity = builder.get_object("quantity") quantity.connect("changed", self.quantity_changed, remaining) quantity_adjustment = builder.get_object("quantity_adjustment") quantity_adjustment.set_lower(0) remaining_adjustment = builder.get_object("remaining_adjustment") remaining_adjustment.set_lower(0) # Connect signals builder.connect_signals({ "on-response": (self.response_add, dialog, item, builder), "on-cancel": (utils.dialog_destroy, dialog) }) query_count_all() dialog.set_transient_for(self.window) dialog.run() dialog.destroy()
def build_kits(self, kits=None): data = parser(self.params, kits) query_count_all() for kit in data: # Create a page child_builder = Gtk.Builder.new_from_file(utils.get_template("first_aid_kit.glade")) # child = child_builder.get_object("main-box") child = child_builder.get_object("child-scrolled") child_builder.connect_signals({ "btn-save-clicked": (self.save_kit, child_builder, kit), "btn-cancel-clicked": (self.cancel, child_builder), "btn-modify-clicked": (self.enable_modify, child_builder) }) name = child_builder.get_object("name") name.set_text(kit["name"]) name.set_sensitive(False) location_combo = child_builder.get_object("location") location_combo.set_sensitive(False) utils.location_combo( combo=location_combo, locations=self.params.locations, active=kit["location_id"] ) btn_save = child_builder.get_object("btn-save") btn_save.hide() self.toggled[kit["id"]] = False self.build_grid(child_builder, kit) self.stack.add_titled(child, "first-aid-kit-{0}".format(kit["id"]), kit["name"]) self.children[kit["id"]] = child_builder
def response_add(self, source, dialog, equipment, builder): fields = { "entry": ["name", "exp_date", "nc_packaging", "remark"], "combobox": ["location"], "spinbutton": ["quantity"], "textview": [] } data = {"parent_id": equipment["id"]} cleaned_data = utils.get_form_data(forms.AddArticleForm, builder, fields, data) if cleaned_data is None: return # Add the article article = models.Article.objects.create( name=cleaned_data['name'], exp_date=cleaned_data['exp_date'], location_id=cleaned_data['location_id'], nc_packaging=cleaned_data['nc_packaging'], parent_id=cleaned_data['parent_id'], remark=cleaned_data['remark']) # Add the quantity models.QtyTransaction.objects.create(transaction_type=1, value=cleaned_data["quantity"], content_object=article) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def response_delete(self, source, location_id, dialog, builder): location = Location.objects.get(id=location_id) location.delete() Location.objects.rebuild() dialog.destroy() self.refresh_grid() query_count_all()
def dialog_add(self, source, equipment): builder = Gtk.Builder.new_from_file( utils.get_template("article_add.glade")) dialog = builder.get_object("dialog") label = builder.get_object("equipment") label.set_text("{0} ({1})".format(equipment["name"], equipment["packaging"])) # Check if equipment has previous locations to input the latest one as # default to ease the input active_location = None equipment_obj = models.Equipment.objects.get(id=equipment["id"]) try: latest_article = equipment_obj.articles.latest("exp_date") except models.Article.DoesNotExist: latest_article = None # except ObjectDoesNotExist: # latest_article = None if latest_article: active_location = latest_article.location.id log.debug("Found last location: %s", active_location) location_combo = builder.get_object("location") utils.location_combo(combo=location_combo, locations=self.params.locations, active=active_location) # By default name = equipment name name = builder.get_object("name") name.set_text(equipment["name"]) # Expiry date input mask workaround exp_date = builder.get_object("exp_date_raw") exp_date = utils.grid_replace(exp_date, widgets.EntryMasked(mask=DATE_MASK)) # exp_date.connect("activate", self.response_add, dialog, equipment, builder) builder.expose_object("exp_date", exp_date) # Connect signals # builder.connect_signals({ # "on_entry_activate": (self.response_add, dialog, equipment, builder) # }) builder.connect_signals({ "on-response": (self.response_add, dialog, equipment, builder), "on-cancel": (utils.dialog_destroy, dialog) }) query_count_all() dialog.set_transient_for(self.window) dialog.run() dialog.destroy()
def dialog_add(self, source, molecule): builder = Gtk.Builder.new_from_file( utils.get_template("medicine_add.glade")) dialog = builder.get_object("dialog") label = builder.get_object("molecule") label.set_text("{0} ({1} - {2})".format(molecule["name"], molecule["dosage_form"], molecule["composition"])) # Check if molecule has previous locations to input the latest one as # default to ease the input active_location = None molecule_obj = models.Molecule.objects.get(id=molecule["id"]) try: latest_medicine = molecule_obj.medicines.latest("exp_date") except models.Medicine.DoesNotExist: latest_medicine = None if latest_medicine: active_location = latest_medicine.location.id log.debug("Found last location: %s", active_location) location_combo = builder.get_object("location") utils.location_combo(combo=location_combo, locations=self.params.locations, active=active_location) # By default name = molecule name name = builder.get_object("name") name.set_text(molecule["name"]) # Expiry date input mask workaround exp_date = builder.get_object("exp_date_raw") exp_date = utils.grid_replace(exp_date, widgets.EntryMasked(mask=DATE_MASK)) # exp_date.connect("activate", self.response_add, dialog, molecule, builder) builder.expose_object("exp_date", exp_date) # Connect signals builder.connect_signals({ # "on-entry-activate": (self.response_add, dialog, molecule, builder), "on-response": (self.response_add, dialog, molecule, builder), "on-cancel": (utils.dialog_destroy, dialog) }) query_count_all() dialog.set_transient_for(self.window) dialog.run() dialog.destroy()
def build_grid(self): self.grid = Gtk.Grid() self.scrolled.add(self.grid) # Header label = Gtk.Label(_("Location"), xalign=0) label.set_hexpand(True) label.get_style_context().add_class("header-cell") self.grid.attach(label, 0, 0, 1, 1) # Add location button on header bar box = Gtk.Box() box.get_style_context().add_class("header-cell-box") btn = Gtk.Button(_("New Location")) btn.set_relief(Gtk.ReliefStyle.NONE) btn.get_style_context().add_class("header-cell-btn") btn.connect("clicked", self.dialog_add) box.add(btn) self.grid.attach(box, 1, 0, 1, 1) location_list = self.params.locations i = 0 for item in location_list: i += 1 prefix = "\t"*(len(item["sequence"]) - 1) label_string = "{0}{1}".format(prefix, item["sequence"][-1]) label = Gtk.Label(label_string, xalign=0) label.get_style_context().add_class("medicine-item-cell") if item["rescue_bag"]: label.get_style_context().add_class("rescue-bag-item") elif item["id"] < 100: label.get_style_context().add_class("location-reserved-item") self.grid.attach(label, 0, i, 1, 1) # We cannot destroy a rescue bag tagged location if not item["rescue_bag"]: self.location_row_buttons(item, i) else: label = Gtk.Label("") label.get_style_context().add_class("medicine-item-buttons") self.grid.attach(label, 1, i, 1, 1) # Check if it is a top level item if len(item["sequence"]) == 1: label.get_style_context().add_class("location-parent") query_count_all()
def update_rescue_bag_model(self, treeview): self.rescue_bag_store = Gtk.ListStore(str, bool, int) # Get allowance data rescue_bag_list = RescueBag.objects.all().order_by("name") for item in rescue_bag_list: # Append to the ListStore self.rescue_bag_store.append((item.name, False, item.id)) treeview.set_model(self.rescue_bag_store) treeview.show_all() query_count_all()
def update_model(self): treeview = self.builder.get_object("allowance-treeview") self.list_store = Gtk.ListStore(str, str, str, bool, int) # Get allowance data allowance_list = Allowance.objects.exclude(id=0).order_by("name") for item in allowance_list: self.list_store.append( (item.name, item.version, item.author, item.active, item.id)) treeview.set_model(self.list_store) query_count_all()
def do_activate(self): # We only allow a single window and raise any existing ones if not self.window: # Windows are associated with the application # when the last one is closed the application shuts down self.window = AppWindow(application=self, params=self.params, actions=self) self.window.set_size_request(800, 600) self.on_dashboard(None, None) self.window.show_all() query_count_all() log.info("Initialization completed!")
def response_delete(self, source, dialog, builder, count): # Get selected RescueBag ID to delete to_delete = [] for row in self.rescue_bag_store: if row[1]: to_delete.append(row[2]) if len(to_delete) != count: return rescue_bags = RescueBag.objects.filter( id__in=to_delete).prefetch_related("location") for rescue_bag in rescue_bags: rescue_bag.location.delete() rescue_bag.delete() query_count_all() dialog.destroy() return True
def response_modify(self, source, dialog, medicine, builder): fields = { "entry": ["name", "exp_date", "nc_composition", "nc_molecule", "remark"], "combobox": ["location"], "spinbutton": ["quantity"], "textview": [] } cleaned_data = utils.get_form_data(forms.ModifyMedicineForm, builder, fields) if cleaned_data is None: return # Add the medicine medicine_obj = models.Medicine.objects.get(id=medicine['id']) medicine_obj.name = cleaned_data['name'] medicine_obj.exp_date = cleaned_data['exp_date'] medicine_obj.location_id = cleaned_data['location_id'] medicine_obj.remark = cleaned_data['remark'] # if cleaned_data['nc_composition']: medicine_obj.nc_composition = cleaned_data['nc_composition'] # if cleaned_data['nc_molecule']: medicine_obj.nc_molecule = cleaned_data['nc_molecule'] medicine_obj.save() if cleaned_data["quantity"] != medicine['quantity']: # Add the quantity (transaction type STOCK COUNT) models.QtyTransaction.objects.create( transaction_type=8, value=cleaned_data["quantity"], content_object=medicine_obj) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def response_modify(self, source, dialog, item, perishable, builder): fields = { "entry": [ "exp_date", "remark", "nc" ], "spinbutton": [ "quantity" ] } data = { "perishable": perishable } cleaned_data = utils.get_form_data(forms.ModifySubitemForm, builder, fields, data) if cleaned_data is None: return item_obj = models.FirstAidKitItem.objects.get(id=item["id"]) item_obj.exp_date = cleaned_data["exp_date"] item_obj.remark = cleaned_data["remark"] item_obj.nc = cleaned_data["nc"] item_obj.save() if cleaned_data["quantity"] != item['quantity']: # Add the quantity (transaction type STOCK COUNT) models.QtyTransaction.objects.create( transaction_type=8, value=cleaned_data["quantity"], content_object=item_obj ) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def dialog_delete(self, total): # Check the real number of RescueBag instances to delete current = RescueBag.objects.count() count = current - total if count <= 0: log.debug( "Number of RescueBag in database is high than requested.") return builder = Gtk.Builder.new_from_file( get_template("rescue_bag_dialog.glade")) dialog = builder.get_object("dialog") # Set message (showing remaining rescue bags to select for deletion) # Adapt for plural form label = builder.get_object("label") btn = builder.get_object("btn-delete") if count < 2: msg_text = _("Select the rescue bag to delete.") btn.set_label(_("Delete this rescue bag")) else: msg_text = _("Select the {0} rescue bags to delete.") btn.set_label(_("Delete these rescue bags")) label.set_text(msg_text.format(count)) # Create the Treeview self.build_rescue_bag_tree(builder, count) # Connect signals builder.connect_signals({ "on-response": (self.response_delete, dialog, builder, count), "on-cancel": (self.dialog_destroy, dialog), }) query_count_all() dialog.run() dialog.destroy()
def response_delete(self, source, dialog, item, builder): invalid = False # Get response reason_combo = builder.get_object("reason") reason = utils.get_reason(reason_combo) if reason is None: reason_combo.get_style_context().add_class("error-combobox") invalid = True if invalid: return # Get the object item_obj = models.FirstAidKitItem.objects.get(id=item['id']) # Reason is Other (error during input ?) if reason == 9: item_obj.delete() # Reason is Perished - it is one way to declare as perished, other way # is to "use" the medicine if reason == 4: item_obj.used = True item_obj.save() models.QtyTransaction.objects.create( transaction_type=4, value=0, content_object=item_obj ) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def required_quantity(data, tar, allowance): """Update the required quantities for deserialized items. After successful deserialization, delete all related required quantity for the selected allowance. Then create all deserialized objects. :param dict data: Dictionnary with filename and model related. The \ following keys must be present: * ``filename``: the name of the JSON file to extract from the tar \ archive; * ``model``: the class of model to deserialize \ (ie: :class:`pharmaship.inventory.models.MoleculeReqQty`). :param tarfile.TarFile tar: tar file archive containing the file to extract :param allowance: allowance instance to rattach :type allowance: models.Allowance :return: ``True`` if there is no error, ``False`` otherwise. :rtype: bool """ log.debug("Updating required quantities for %s", data["filename"]) deserialized_list = deserialize_json_file(data, tar, allowance) if deserialized_list is False: log.error("Error when deserializing file: %s", data["filename"]) return False # As we are sure that the deserialization went fine, # delete all required quantities entry and create new ones data["model"].objects.filter(allowance_id__in=(0, allowance.id)).delete() data["model"].objects.bulk_create(deserialized_list) log.debug("Created %s instances", len(deserialized_list)) query_count_all() return True
def dialog_add(self, source): log.debug("Add location") builder = Gtk.Builder.new_from_file(utils.get_template("location_add.glade")) dialog = builder.get_object("dialog") location_combo = builder.get_object("parent") utils.location_combo( location_combo, locations=self.params.locations, empty=True ) builder.connect_signals({ "on-response": (self.response_add, dialog, builder), "on-cancel": (utils.dialog_destroy, dialog) }) query_count_all() dialog.set_transient_for(self.window) dialog.run() dialog.destroy()
def response_modify(self, source, dialog, article, builder): fields = { "entry": ["name", "exp_date", "nc_packaging", "remark"], "combobox": ["location"], "spinbutton": ["quantity"] } cleaned_data = utils.get_form_data(forms.ModifyArticleForm, builder, fields) if cleaned_data is None: return # Add the article article_obj = models.Article.objects.get(id=article['id']) article_obj.name = cleaned_data['name'] article_obj.exp_date = cleaned_data['exp_date'] article_obj.location_id = cleaned_data['location_id'] article_obj.remark = cleaned_data['remark'] article_obj.nc_packaging = cleaned_data['nc_packaging'] article_obj.save() if cleaned_data["quantity"] != article['quantity']: # Add the quantity (transaction type STOCK COUNT) models.QtyTransaction.objects.create( transaction_type=8, value=cleaned_data["quantity"], content_object=article_obj) query_count_all() # At the end only dialog.destroy() # Refresh the list! self.refresh_grid()
def serialize_allowance(allowance, content_types): """Export an allowance using the YAML format. To have an usable export, the broadcaster needs: - the :model:`pharmaship.inventory.Allowance` selected instance, And related to this instance: - the :model:`pharmaship.inventory.Molecule` objects list, - the :model:`pharmaship.inventory.Equipment` objects list, - the :model:`pharmaship.inventory.MoleculeReqQty` objects list, - the :model:`pharmaship.inventory.EquipmentReqQty` objects list, - the :model:`pharmaship.inventory.RescueBagReqQty` objects list, - the :model:`pharmaship.inventory.FirstAidKitReqQty` objects list, - the :model:`pharmaship.inventory.TelemedicalReqQty` objects list, - the :model:`pharmaship.inventory.LaboratoryReqQty` objects list. Returns a list of filenames and streams. """ log.debug("Start serialize") renderer = JSONRenderer() # Molecules used by the allowance molecule_id_list = [] equipment_id_list = [] # Required quantities for molecules molecule_reqqty_list = pharmaship.inventory.models.MoleculeReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") molecule_id_list += molecule_reqqty_list.values_list("base_id", flat=True) # molecule_reqqty_data = serializers.serialize( # "yaml", # molecule_reqqty_list, # fields=('base', 'required_quantity'), # use_natural_foreign_keys=True # ) # query_count_all() serialized = pharmaship.inventory.serializers.MoleculeReqQtySerializer(molecule_reqqty_list, many=True) molecule_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Required quantities for equipments equipment_reqqty_list = pharmaship.inventory.models.EquipmentReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") equipment_id_list += equipment_reqqty_list.values_list("base_id", flat=True) # equipment_reqqty_data = serializers.serialize( # "yaml", # equipment_reqqty_list, # fields=('base', 'required_quantity'), # use_natural_foreign_keys=True # ) # query_count_all() serialized = pharmaship.inventory.serializers.EquipmentReqQtySerializer(equipment_reqqty_list, many=True) equipment_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Required quantities for Laboratory laboratory_reqqty_list = pharmaship.inventory.models.LaboratoryReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") equipment_id_list += laboratory_reqqty_list.values_list("base_id", flat=True) # laboratory_reqqty_data = serializers.serialize( # "yaml", # laboratory_reqqty_list, # fields=('base', 'required_quantity'), # use_natural_foreign_keys=True # ) # query_count_all() serialized = pharmaship.inventory.serializers.LaboratoryReqQtySerializer(laboratory_reqqty_list, many=True) laboratory_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Required quantities for Telemedical telemedical_reqqty_list = pharmaship.inventory.models.TelemedicalReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") equipment_id_list += telemedical_reqqty_list.values_list("base_id", flat=True) # telemedical_reqqty_data = serializers.serialize( # "yaml", # telemedical_reqqty_list, # fields=('base', 'required_quantity'), # use_natural_foreign_keys=True # ) # query_count_all() serialized = pharmaship.inventory.serializers.TelemedicalReqQtySerializer(telemedical_reqqty_list, many=True) telemedical_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Required quantities for First Aid Kit first_aid_kit_reqqty_list = pharmaship.inventory.models.FirstAidKitReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") molecule_id_list += first_aid_kit_reqqty_list.filter( content_type_id=content_types["molecule"] ).values_list("object_id", flat=True) equipment_id_list += first_aid_kit_reqqty_list.filter( content_type_id=content_types["equipment"] ).values_list("object_id", flat=True) serialized = pharmaship.inventory.serializers.FirstAidKitReqQtySerializer(first_aid_kit_reqqty_list, many=True) first_aid_kit_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Required quantities for Rescue Bag rescue_bag_reqqty_list = pharmaship.inventory.models.RescueBagReqQty.objects.filter(allowance__in=[allowance]).prefetch_related("base") molecule_id_list += rescue_bag_reqqty_list.filter( content_type_id=content_types["molecule"] ).values_list("object_id", flat=True) equipment_id_list += rescue_bag_reqqty_list.filter( content_type_id=content_types["equipment"] ).values_list("object_id", flat=True) serialized = pharmaship.inventory.serializers.RescueBagReqQtySerializer(rescue_bag_reqqty_list, many=True) rescue_bag_reqqty_data = renderer.render( data=serialized.data, accepted_media_type='application/json; indent=2' ) query_count_all() # Equipment used by the allowance equipment_list = pharmaship.inventory.models.Equipment.objects.filter(id__in=equipment_id_list).prefetch_related("group") equipment_data = serializers.serialize( "yaml", equipment_list, use_natural_foreign_keys=True, fields=("name", "packaging", "consumable", "perishable", "picture", "group", "remark") ) log.debug("Equipment") query_count_all() # Molecule used by the allowance molecule_list = pharmaship.inventory.models.Molecule.objects.filter(id__in=molecule_id_list).prefetch_related("group") molecule_data = serializers.serialize( "yaml", molecule_list, use_natural_foreign_keys=True, fields=("name", "roa", "dosage_form", "composition", "medicine_list", "group", "remark") ) log.debug("Molecule") query_count_all() # Allowance record allowance_data = serializers.serialize( "yaml", (allowance,), fields=('name', 'author', 'version', 'date', 'additional'), use_natural_foreign_keys=True ) log.debug("Allowance") query_count_all() log.debug("End serialize") # Returning a list with tuples: (filename, data) return ([ ('inventory/molecule_obj.yaml', remove_yaml_pk(molecule_data)), ('inventory/equipment_obj.yaml', remove_yaml_pk(equipment_data)), ('inventory/molecule_reqqty.json', molecule_reqqty_data), ('inventory/equipment_reqqty.json', equipment_reqqty_data), ('inventory/laboratory_reqqty.json', laboratory_reqqty_data), ('inventory/telemedical_reqqty.json', telemedical_reqqty_data), ('inventory/first_aid_kit_reqqty.json', first_aid_kit_reqqty_data), ('inventory/rescue_bag_reqqty.json', rescue_bag_reqqty_data), ('inventory/allowance.yaml', remove_yaml_pk(allowance_data)), ], equipment_list)
def create_grid(self, toggle_row_num=None): grid = Gtk.Grid() # Header label = Gtk.Label(_("Name"), xalign=0) label.set_hexpand(True) label.get_style_context().add_class("header-cell") grid.attach(label, 0, 0, 1, 1) label = Gtk.Label(_("Remarks"), xalign=0) label.set_hexpand(True) label.get_style_context().add_class("header-cell") grid.attach(label, 1, 0, 1, 1) label = Gtk.Label(_("Packaging"), xalign=0) label.set_hexpand(True) label.get_style_context().add_class("header-cell") grid.attach(label, 2, 0, 1, 1) label = Gtk.Label(_("Location"), xalign=0) label.get_style_context().add_class("header-cell") grid.attach(label, 3, 0, 1, 1) label = Gtk.Label(_("Expiry"), xalign=0.5) label.get_style_context().add_class("header-cell") grid.attach(label, 4, 0, 1, 1) label = Gtk.Label(_("Quantity"), xalign=0.5) label.get_style_context().add_class("header-cell") grid.attach(label, 5, 0, 1, 1) label = Gtk.Label("", xalign=0) label.get_style_context().add_class("header-cell") # Size request because by default the colum content is "hidden" label.set_size_request(125, -1) grid.attach(label, 6, 0, 1, 1) data = parser(self.params) i = 0 toggle_equipment = None for equipment in data: i += 1 # If toggle_row_num is defined, record first the equipment then, when # all construction is done, call toggle_article function. if toggle_row_num and toggle_row_num == i: toggle_equipment = equipment if self.chosen and self.chosen == equipment["id"]: toggle_equipment = equipment toggle_row_num = i self.row_widget_num = i label = Gtk.Label(equipment["name"], xalign=0) label.set_line_wrap(True) label.set_lines(1) label.set_line_wrap_mode(2) label.get_style_context().add_class("item-cell") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 0, i, 1, 1) label = Gtk.Label(equipment["remark"], xalign=0) label.set_line_wrap(True) label.set_lines(1) label.set_line_wrap_mode(2) label.get_style_context().add_class("item-cell") label.get_style_context().add_class("article-remark") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 1, i, 1, 1) label = Gtk.Label(equipment["packaging"], xalign=0) label.get_style_context().add_class("item-cell") label.set_line_wrap(True) label.set_lines(1) label.set_line_wrap_mode(2) evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 2, i, 1, 1) # Get list of locations locations_len = len(equipment["locations"]) if locations_len == 0: locations_display = "" elif locations_len >= 1: equipment["locations"].sort() locations_display = equipment["locations"][0] if locations_len > 1: locations_display += ", ..." label = Gtk.Label(locations_display, xalign=0) label.set_line_wrap(True) label.set_lines(1) label.set_line_wrap_mode(2) label.get_style_context().add_class("item-cell") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 3, i, 1, 1) # Get first expiry date date_display = "" if len(equipment["exp_dates"] ) > 0 and None not in equipment["exp_dates"]: date_display = min(equipment["exp_dates"]).strftime("%Y-%m-%d") label = Gtk.Label(date_display, xalign=0.5) label.get_style_context().add_class("item-cell") label.get_style_context().add_class("text-mono") if equipment["has_date_expired"]: label.get_style_context().add_class("article-expired") elif equipment["has_date_warning"]: label.get_style_context().add_class("article-warning") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 4, i, 1, 1) label = Gtk.Label(xalign=0.5) label.set_markup("{0}<small>/{1}</small>".format( equipment["quantity"], equipment["required_quantity"])) label.get_style_context().add_class("item-cell") label.get_style_context().add_class("text-mono") # Change style if equipment has articles with non-conformity if equipment["has_nc"]: label.get_style_context().add_class("item-nc-quantity") # If quantity is less than required, affect corresponding style if equipment["quantity"] < equipment["required_quantity"]: label.get_style_context().add_class("article-expired") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 5, i, 1, 1) # Set tooltip to give information on allowances requirements tooltip_text = [] for item in equipment["allowance"]: tooltip_text.append("<b>{0}</b> ({1})".format( item["name"], item["quantity"])) label.set_tooltip_markup("\n".join(tooltip_text)) if equipment["picture"]: # Button box for actions linked_btn = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) linked_btn.get_style_context().add_class("linked") linked_btn.get_style_context().add_class( "equipment-item-buttons") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(linked_btn) grid.attach(evbox, 6, i, 1, 1) # Picture picture = equipment["picture"] btn_picture = widgets.ButtonWithImage( "image-x-generic-symbolic", tooltip=_("View picture"), connect=utils.picture_frame, data=picture) linked_btn.pack_end(btn_picture, False, True, 0) else: label = Gtk.Label("", xalign=0.5) label.get_style_context().add_class("item-cell") evbox = widgets.EventBox(equipment, self.toggle_article, 7, i) evbox.add(label) grid.attach(evbox, 6, i, 1, 1) # Toggle if active if toggle_row_num and toggle_equipment: self.toggle_article(source=None, grid=grid, equipment=toggle_equipment, row_num=toggle_row_num) query_count_all() return grid
def toggle_article(self, source, grid, equipment, row_num): # If already toggled, destroy the toggled part if self.toggled and self.toggled[0] > 0: # Remove the active-row CSS class of the parent item utils.grid_row_class(grid, self.toggled[0] - 1, 7, False) for i in range(self.toggled[1] - self.toggled[0] + 1): grid.remove_row(self.toggled[0]) # No need to recreate the widget, we just want to hide if row_num + 1 == self.toggled[0]: self.toggled = False return True # Add the active-row CSS class utils.grid_row_class(grid, row_num, 7) # Need to create the content new_row = row_num + 1 grid.insert_row(new_row) # Header row label = Gtk.Label(_("Commercial Name"), xalign=0) label.set_hexpand(True) label.get_style_context().add_class("article-header-cell") grid.attach(label, 0, 0 + new_row, 1, 1) label = Gtk.Label(_("Remarks"), xalign=0) label.get_style_context().add_class("article-header-cell") grid.attach(label, 1, 0 + new_row, 2, 1) label = Gtk.Label(_("Location"), xalign=0) label.get_style_context().add_class("article-header-cell") grid.attach(label, 3, 0 + new_row, 1, 1) label = Gtk.Label(_("Expiry"), xalign=0.5) label.get_style_context().add_class("article-header-cell") grid.attach(label, 4, 0 + new_row, 1, 1) label = Gtk.Label(_("Quantity"), xalign=0.5) label.get_style_context().add_class("article-header-cell") grid.attach(label, 5, 0 + new_row, 1, 1) label = Gtk.Label(_("Actions"), xalign=1) label.get_style_context().add_class("article-header-cell") grid.attach(label, 6, 0 + new_row, 1, 1) # Get related articles articles = equipment["articles"] i = new_row for article in articles: i += 1 grid.insert_row(i) label = Gtk.Label(article["name"], xalign=0) label.set_hexpand(True) label.get_style_context().add_class("article-item-cell-name") label.get_style_context().add_class("article-item-cell") grid.attach(label, 0, i, 1, 1) # Remark field (mainly used for non-compliance) remark_text = [] if article["nc_packaging"]: remark_text.append( NC_TEXT_TEMPLATE.format(_("Non-compliant packaging:"), article["nc_packaging"])) if article["remark"]: remark_text.append(article["remark"]) label = Gtk.Label(xalign=0) label.set_markup("\n".join(remark_text)) label.get_style_context().add_class("article-item-cell") label.get_style_context().add_class("article-remark") grid.attach(label, 1, i, 2, 1) label = Gtk.Label(xalign=0) label.get_style_context().add_class("article-item-cell") sequence = article["location"]["sequence"] if len(sequence) > 1: parents = " > ".join(sequence[:-1]) location_display = "<span foreground=\"#555\">{0} > </span>{1}".format( parents, sequence[-1]) else: location_display = sequence[0] label.set_markup(location_display) label.set_line_wrap(True) label.set_lines(1) label.set_line_wrap_mode(2) grid.attach(label, 3, i, 1, 1) if article["exp_date"]: label = Gtk.Label(article["exp_date"].strftime("%Y-%m-%d"), xalign=0.5) label.get_style_context().add_class("text-mono") else: label = Gtk.Label() label.get_style_context().add_class("article-item-cell") # If expiry is soon or due, affect corresponding style if article["expired"]: label.get_style_context().add_class("article-expired") elif article["warning"]: label.get_style_context().add_class("article-warning") grid.attach(label, 4, i, 1, 1) label = Gtk.Label(article["quantity"], xalign=0.5) label.get_style_context().add_class("article-item-cell") label.get_style_context().add_class("text-mono") grid.attach(label, 5, i, 1, 1) # Button box for actions linked_btn = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) linked_btn.get_style_context().add_class("linked") linked_btn.get_style_context().add_class("article-item-buttons") # linked_btn.set_halign(Gtk.Align.END) grid.attach(linked_btn, 6, i, 1, 1) # Use if equipment["consumable"]: btn_use = widgets.ButtonWithImage("edit-redo-symbolic", tooltip=_("Use"), connect=self.dialog_use, data=article) linked_btn.pack_end(btn_use, False, True, 0) # Modify btn_modify = widgets.ButtonWithImage("document-edit-symbolic", tooltip=_("Modify"), connect=self.dialog_modify, data=article) linked_btn.pack_end(btn_modify, False, True, 0) # Delete btn_delete = widgets.ButtonWithImage("edit-delete-symbolic", tooltip=_("Delete"), connect=self.dialog_delete, data=article) btn_delete.get_style_context().add_class("article-btn-delete") linked_btn.pack_end(btn_delete, False, True, 0) i += 1 grid.insert_row(i) box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) button = Gtk.Button() label = Gtk.Label(_("Add an article"), xalign=0) button.add(label) button.set_relief(Gtk.ReliefStyle.NONE) button.get_style_context().add_class("article-btn-add") button.connect("clicked", self.dialog_add, equipment) box.add(button) box.get_style_context().add_class("article-item-cell-add") grid.attach(box, 0, i, 1, 1) # Empty row for styling purpose label = Gtk.Label("") label.get_style_context().add_class("article-item-cell-add") grid.attach(label, 1, i, 6, 1) grid.show_all() self.toggled = (new_row, i) query_count_all()