def process_area_access_record_with_parents(user: User): show_not_qualified_areas = get_customization( 'dashboard_display_not_qualified_areas') records = AreaAccessRecord.objects.filter(end=None, staff_charge=None) if not user.is_staff and show_not_qualified_areas != 'enabled': records = records.filter(area__in=user.accessible_areas()) records = records.prefetch_related('customer', 'project', 'area') no_occupants = not records.exists() area_items = None area_model_tree = get_area_model_tree() if not no_occupants: areas_and_parents = area_model_tree.get_ancestor_areas( area_model_tree.get_areas([record.area.id for record in records]), include_self=True) # Sort to have area without children before others areas_and_parents.sort(key=lambda x: f'{x.tree_category}zz' if x.is_leaf else f'{x.tree_category}/aa') area_summary = create_area_summary(area_model_tree=area_model_tree, add_resources=False, add_outages=False, add_occupants=True) area_summary_dict = {area['id']: area for area in area_summary} for area_item in areas_and_parents: area_item.item = area_summary_dict[area_item.id] area_items = area_tree_helper(areas_and_parents, records) return area_items, no_occupants
def render(self, name, value, attrs=None, renderer=None): """ This widget takes a list of items (tools/areas) and creates nested unordered lists in a hierarchical manner. The parameters name and attrs are not used. 'value' is a dictionary which must contain a 'tools' or 'areas' key with a value that is a QuerySet of all tools/areas to be put in the list. A collection of unordered HTML lists is returned with various callbacks and properties attached to each nested element. For a more concrete example, suppose the following tools are input to the item tree: Packaging/Dicing Saw Chemical Vapor Deposition/PECVD Gen Furnaces/Sinter The following unordered HTML list would be produced: <ul> <li> <a href="javascript:void(0);" onclick="on_item_tree_click($(this.nextSibling))" class="node">Packaging</a> <ul class="collapsed"> <li><a href="javascript:void(0);" onclick="on_item_tree_click($(this))" class="leaf node">Dicing saw</a></li> </ul> </li> <li> <a href="javascript:void(0);" onclick="on_item_tree_click($(this.nextSibling))" class="node">Chemical Vapor Deposition</a> <ul class="collapsed"> <li><a href="javascript:void(0);" onclick="on_item_tree_click($(this))" class="leaf node">PECVD</a></li> </ul> </li> <li> <a href="javascript:void(0);" onclick="on_item_tree_click($(this.nextSibling))" class="node">Gen Furnaces</a> <ul class="collapsed"> <li><a href="javascript:void(0);" onclick="on_item_tree_click($(this))" class="leaf node">Sinter</a></li> </ul> </li> </ul> """ area_tree = ItemTreeHelper(None, ReservationItemType.AREA) tool_tree = ItemTreeHelper(None, ReservationItemType.TOOL) user: User = value['user'] if 'user' in value else None model_tree = get_area_model_tree() area_tree_items: List[TreeItem] = model_tree.get_areas( [area.id for area in value.get('areas', [])]) tools: List[Tool] = value.get('tools', []) tool_parent_ids = Tool.objects.filter( parent_tool__isnull=False).values_list('parent_tool_id', flat=True) user_accessible_areas = [] if not user or not area_tree_items else user.accessible_areas( ) user_qualified_tool_ids = [] if not user or not tools else user.qualifications.all( ).values_list('id', flat=True) parent_areas_dict = {} if area_tree_items: # Create a lookup of area name to area with all the parents (in order to display info about category-parents) parent_areas_dict = { area_tree_item.name: area_tree_item for area_tree_item in model_tree.get_ancestor_areas( area_tree_items) } # Sort areas by complete category area_tree_items = list(area_tree_items) area_tree_items.sort(key=lambda area: area.tree_category) display_all_areas = get_customization( 'calendar_display_not_qualified_areas') == 'enabled' for area in area_tree_items: category = area.tree_category + '/' if area.tree_category else '' is_qualified = True if not display_all_areas else ( user and user.is_staff) or ( user and area.item in user_accessible_areas) area_tree.add(ReservationItemType.AREA, category + area.name, area.id, is_qualified) for tool in tools: is_qualified = (user and user.is_staff) or ( user and tool.id in user_qualified_tool_ids) tool_tree.add( ReservationItemType.TOOL, tool.category + '/' + tool.name_or_child_in_use_name(parent_ids=tool_parent_ids), tool.id, is_qualified) legend = True if area_tree_items and tools else False result = "" if area_tree_items: result += area_tree.render(legend=legend, category_items_lookup=parent_areas_dict) if tools: result += tool_tree.render(legend=legend) return mark_safe(result)
def create_area_summary(area_model_tree: ModelTreeHelper = None, add_resources=True, add_occupants=True, add_outages=True): if area_model_tree is None: area_model_tree = get_area_model_tree() area_items = area_model_tree.items.values() result = {} for area in area_items: result[area.id] = { 'name': area.name, 'id': area.id, 'maximum_capacity': area.maximum_capacity, 'warning_capacity': area.item.warning_capacity(), 'danger_capacity': area.item.danger_capacity(), 'count_staff_in_occupancy': area.count_staff_in_occupancy, 'count_service_personnel_in_occupancy': area.count_service_personnel_in_occupancy, 'occupancy_count': 0, 'occupancy': 0, 'occupancy_staff': 0, 'occupancy_service_personnel': 0, 'occupants': '', 'required_resource_is_unavailable': False, 'scheduled_outage': False, } if add_resources: unavailable_resources = Resource.objects.filter( available=False).prefetch_related( Prefetch('dependent_areas', queryset=Area.objects.only('id'))) for resource in unavailable_resources: for area in resource.dependent_areas.all(): if area.id in result: result[area.id]['required_resource_is_unavailable'] = True if add_outages: scheduled_outages = ScheduledOutage.objects.filter( start__lte=timezone.now(), end__gt=timezone.now(), tool__isnull=True).only('area_id', 'resource_id') for outage in scheduled_outages: if outage.area_id: result[outage.area_id]['scheduled_outage'] = True elif outage.resource_id: for t in outage.resource.dependent_areas.values_list( 'id', flat=True): result[t]['scheduled_outage'] = True if add_occupants: occupants: List[AreaAccessRecord] = AreaAccessRecord.objects.filter( end=None, staff_charge=None).prefetch_related( Prefetch('customer', queryset=User.objects.all().only( 'first_name', 'last_name', 'username', 'is_staff'))) for occupant in occupants: # Get ids for area and all the parents (so we can add occupants info on parents) area_ids = area_model_tree.get_area( occupant.area_id).ancestor_ids(include_self=True) if occupant.customer.is_staff: customer_display = f'<span class="success-highlight">{str(occupant.customer)}</span>' elif occupant.customer.is_service_personnel: customer_display = f'<span class="warning-highlight">{str(occupant.customer)}</span>' elif occupant.customer.is_logged_in_area_without_reservation(): customer_display = f'<span class="danger-highlight">{str(occupant.customer)}</span>' else: customer_display = str(occupant.customer) for area_id in area_ids: if area_id in result: result[area_id]['occupancy'] += 1 if occupant.customer.is_staff: result[area_id]['occupancy_staff'] += 1 if occupant.customer.is_service_personnel: result[area_id]['occupancy_service_personnel'] += 1 if (not occupant.customer.is_staff or result[area_id]['count_staff_in_occupancy'] ) and (not occupant.customer.is_service_personnel or result[area_id] ['count_service_personnel_in_occupancy']): result[area_id]['occupancy_count'] += 1 result[area_id][ 'occupants'] += customer_display if not result[ area_id]['occupants'] else f'<br>{customer_display}' area_summary = list(result.values()) area_summary.sort(key=lambda x: x['name']) return area_summary