def get_queryset(self): today = date.today() queryset = AktivitetDate.get_searchable().filter( start_date__gte=today ).select_related( 'aktivitet', 'aktivitet__organizer_forening', ).prefetch_related( 'participant_groups', 'participant_groups__participants', 'sherpa2_participants', 'aktivitet__counties', 'aktivitet__municipalities', 'aktivitet__co_organizers_forening', 'aktivitet__organizer_forening__sites', 'aktivitet__images', 'aktivitet__category_tags', ).distinct() return queryset.all()
def parse(self, widget_options, site): aktivitet_dates = AktivitetDate.get_published().filter( Q(aktivitet__forening__in=widget_options['foreninger']) | Q(aktivitet__co_foreninger__in=widget_options['foreninger']), aktivitet__private=False, start_date__gte=date.today(), ) url_query_parameters = [] limit = widget_options.get('limit', 50) total = aktivitet_dates.count() more_activities_count = total - limit # Skip if none, or all, audiences were chosen if len(widget_options['audiences']) > 0 and len(widget_options['audiences']) < len(AktivitetAudience.AUDIENCE_CHOICES): aktivitet_dates = aktivitet_dates.filter(aktivitet__audiences__name__in=widget_options['audiences']) url_query_parameters.append("audiences=%s" % ",".join(widget_options['audiences'])) # Skip if none, or all, categories were chosen if len(widget_options['categories']) > 0 and len(widget_options['categories']) < len(Aktivitet.CATEGORY_CHOICES): aktivitet_dates = aktivitet_dates.filter( aktivitet__category__in=widget_options['categories'], ) url_query_parameters.append("categories=%s" % ",".join(widget_options['categories'])) if len(widget_options['foreninger']) > 0: organizers = ["forening:%s" % f for f in widget_options['foreninger']] url_query_parameters.append("organizers=%s" % ",".join(organizers)) aktivitet_dates = aktivitet_dates.order_by('start_date')[:limit] see_more_query_params = "&".join(url_query_parameters) return { 'aktivitet_dates': aktivitet_dates, 'see_more_query_params': see_more_query_params, 'total': total, 'limit': limit, 'more_activities_count': more_activities_count }
def filter_aktivitet_dates(filter, user, active_forening, requested_forening): """ Filters AktivitetDates based on: - The provided query parameters - The current user and their permissions - The currently active forening (note that this differs from the requested forening. Forening-admins may request aktivitetdates for a child-forening) Returns AktivitetDate objects. """ if filter.get('kladd') == 'false': dates = AktivitetDate.get_searchable() else: dates = AktivitetDate.objects dates = dates.select_related( 'aktivitet', 'aktivitet__organizer_forening', ).prefetch_related( 'participant_groups', 'participant_groups__participants', 'sherpa2_participants', ) if filter.get('sok'): dates = dates.filter( Q(aktivitet__title__icontains=filter.get('sok')) | Q(aktivitet__code=filter.get('sok')) ) today = date.today() if filter.get('tid') in ['denne_uke', 'neste_uke', 'neste_maned']: if filter.get('tid') == 'denne_uke': start = today - timedelta(days=today.weekday()) end = today + timedelta(days=-start.weekday(), weeks=1) elif filter.get('tid') == 'neste_uke': start = today + timedelta(days=-today.weekday(), weeks=1) end = today + timedelta(days=-today.weekday(), weeks=2) else: (start_year, start_month) = divmod(today.month, 12) (end_year, end_month) = divmod(today.month + 1, 12) start = today.replace(year=today.year + start_year, month=start_month + 1, day=1) end = today.replace(year=today.year + end_year, month=end_month + 1, day=1) dates = dates.filter(start_date__gte=start, start_date__lt=end) elif filter.get('tid') != 'alle': dates = dates.filter(start_date__gte=today) dates = dates.order_by('start_date').distinct() # If the user is admin, include dates organized by the children of the active forening if user.is_admin_in_forening(active_forening): dates = dates.filter( Q(aktivitet__organizer_forening=requested_forening) | Q(aktivitet__co_organizers_forening=requested_forening) | Q( aktivitet__organizer_forening__parents=requested_forening, aktivitet__organizer_forening__type='turgruppe', ) | Q( aktivitet__co_organizers_forening__parents=requested_forening, aktivitet__co_organizers_forening__type='turgruppe', ) ) else: dates = dates.filter( Q(aktivitet__organizer_forening=requested_forening) | Q(aktivitet__co_organizers_forening=requested_forening) ) return dates
def filter_aktivitet_dates(filter): # To the next mainainer: The filter param is a mutateable query dict such that this util method # can split strings into lists which the template logic is dependent upon. If you find a better # way, please refactor this code. dates = AktivitetDate.get_published().select_related( 'aktivitet__forening', ).prefetch_related( 'aktivitet', 'aktivitet__images', 'aktivitet__forening__sites', 'aktivitet__co_foreninger', ).filter(aktivitet__private=False) if filter.get('search', '') and len(filter['search'].strip()) > 2: words = filter['search'].split() dates = dates.filter( Q(reduce(lambda x, y: x & y, [Q(aktivitet__title__icontains=word) | Q(aktivitet__description__icontains=word) for word in words])) | Q(aktivitet__code=filter['search']) ) if filter.get('omrader', ''): filter['omrader'] = filter['omrader'].split(',') for omrade in filter['omrader']: dates = dates.extra( where=['%s = ANY ("{0}"."omrader")'.format(Aktivitet._meta.db_table)], params=[omrade], ) if filter.get('categories', ''): filter['categories'] = filter['categories'].split(',') dates = dates.filter(aktivitet__category__in=filter['categories']) if filter.get('category_types', ''): filter['category_types'] = filter['category_types'].split(',') # Note that we're checking for both types and tags, and since objects may have the same tag specified twice, # it'll require an explicit distinct clause in our query dates = dates.filter( Q(aktivitet__category_type__in=filter['category_types']) | Q(aktivitet__category_tags__name__in=filter['category_types']) ) if filter.get('audiences', ''): filter['audiences'] = filter['audiences'].split(',') dates = dates.filter(aktivitet__audiences__name__in=filter['audiences']) if filter.get('difficulties', ''): filter['difficulties'] = filter['difficulties'].split(',') dates = dates.filter(aktivitet__difficulty__in=filter['difficulties']) if filter.get('lat_lng', '') and len(filter['lat_lng'].split(',')) == 2: filter['lat_lng'] = filter['lat_lng'].split(',') # Rule of thumb for buffer; 1 degree is about 100 km boundary = geos.Point(float(filter['lat_lng'][0]), float(filter['lat_lng'][1])).buffer(0.5) dates = dates.filter(aktivitet__start_point__within=boundary) # @TODO refactor to make use of django range query # https://docs.djangoproject.com/en/dev/ref/models/querysets/#range try: if filter.get('start_date', ''): dates = dates.filter(start_date__gte=datetime.strptime(filter['start_date'], "%d.%m.%Y")) else: today = date.today() dates = dates.filter(start_date__gte=datetime(today.year, today.month, today.day)) if filter.get('end_date'): dates = dates.filter(end_date__lte=datetime.strptime(filter['end_date'], "%d.%m.%Y")) except (ValueError, KeyError): pass if filter.get('organizers', ''): filter['organizers'] = filter['organizers'].split(',') filter['foreninger'] = [] filter['cabins'] = [] for organizer in filter['organizers']: try: type, id = organizer.split(':') if type == 'forening': filter['foreninger'].append(int(id)) elif type == 'cabin': filter['cabins'].append(int(id)) except ValueError: continue if filter['foreninger']: dates = dates.filter( Q(aktivitet__forening__in=filter['foreninger']) | Q(aktivitet__co_foreninger__in=filter['foreninger']) ) if filter['cabins']: dates = dates.filter( Q(aktivitet__forening_cabin__in=filter['cabins']) | Q(aktivitet__co_foreninger_cabin__in=filter['cabins']) ) dates = dates.distinct().order_by( 'start_date' ) return filter, dates
def filter_aktivitet_dates(filter): # To the next mainainer: The filter param is a mutateable query dict such that this util method # can split strings into lists which the template logic is dependent upon. If you find a better # way, please refactor this code. dates = AktivitetDate.get_searchable().select_related( 'aktivitet', 'aktivitet__organizer_forening', ).prefetch_related( 'aktivitet__images', 'aktivitet__organizer_forening__sites', 'aktivitet__co_organizers_forening', 'aktivitet__category_tags', ) if filter.get('search', '') and len(filter['search'].strip()) > 2: words = filter['search'].split() dates = dates.filter( Q(reduce(lambda x, y: x & y, [ Q(aktivitet__title__icontains=word) | Q(aktivitet__description__icontains=word) | Q(aktivitet__title_en__icontains=word) | Q(aktivitet__description_en__icontains=word) for word in words ])) | Q(aktivitet__code=filter['search']) ) if filter.get('omrader', ''): filter['omrader'] = filter['omrader'].split(',') # Build the where clause to search all omrader for each filtered omrade # Use a list with the ANY construct repeated for each filtered omrade, and join them with OR constructs = ['%s = ANY ("omrader")'] * len(filter['omrader']) where_clause = ' OR '.join(constructs) dates = dates.extra( where=[where_clause], params=filter['omrader'], ) if filter.get('categories', ''): filter['categories'] = filter['categories'].split(',') dates = dates.filter(aktivitet__category__in=filter['categories']) if filter.get('category_types', ''): filter['category_types'] = filter['category_types'].split(',') # Note that we're checking for both types and tags, and since objects may have the same tag specified twice, # it'll require an explicit distinct clause in our query dates = dates.filter( Q(aktivitet__category_type__in=filter['category_types']) | Q(aktivitet__category_tags__name__in=filter['category_types']) ) # Note that the UI doesn't expose a control for the category_tags filter, but it can be used in widgets, which in # turn links to the full search with the same query parameters. if filter.get('category_tags', ''): filter['category_tags'] = filter['category_tags'].split(',') dates = dates.filter( aktivitet__category_tags__name__in=filter['category_tags'], ) if filter.get('audiences', ''): filter['audiences'] = filter['audiences'].split(',') # Note the usage of 'overlap' here to combine multiple audiences by 'or' dates = dates.filter(aktivitet__audiences__overlap=filter['audiences']) if filter.get('difficulties', ''): filter['difficulties'] = filter['difficulties'].split(',') dates = dates.filter(aktivitet__difficulty__in=filter['difficulties']) try: if filter.get('lat_lng', '') and len(filter['lat_lng'].split(',')) == 2: filter['lat_lng'] = filter['lat_lng'].split(',') # Rule of thumb for buffer; 1 degree is about 100 km boundary = geos.Point(float(filter['lat_lng'][0]), float(filter['lat_lng'][1])).buffer(0.5) dates = dates.filter(aktivitet__start_point__within=boundary) except ValueError: pass # @TODO refactor to make use of django range query # https://docs.djangoproject.com/en/dev/ref/models/querysets/#range try: if filter.get('start_date', ''): dates = dates.filter(start_date__gte=datetime.strptime(filter['start_date'], "%d.%m.%Y").date()) else: dates = dates.filter(start_date__gte=date.today()) if filter.get('end_date'): dates = dates.filter(end_date__lte=datetime.strptime(filter['end_date'], "%d.%m.%Y").date()) except (ValueError, KeyError): pass if filter.get('min_duration'): try: dates = dates.filter(duration_days__gte=filter['min_duration']) except ValueError: pass if filter.get('max_duration'): try: dates = dates.filter(duration_days__lte=filter['max_duration']) except ValueError: pass if filter.get('organizers', ''): filter['organizers'] = filter['organizers'].split(',') filter['foreninger'] = [] filter['cabins'] = [] for organizer in filter['organizers']: try: type, id = organizer.split(':') if type == 'forening': filter['foreninger'].append(int(id)) elif type == 'cabin': filter['cabins'].append(id) except ValueError: continue if filter['foreninger'] or filter['cabins']: dates = dates.filter( Q(aktivitet__organizer_forening__in=filter['foreninger']) | Q(aktivitet__co_organizers_forening__in=filter['foreninger']) | Q(aktivitet__organizer_cabin__in=filter['cabins']) | Q(aktivitet__co_organizers_cabin__overlap=filter['cabins']) ) dates = dates.distinct() return filter, dates
def edit(request, aktivitet): if request.method == 'GET': aktivitet = Aktivitet.objects.prefetch_related('municipalities', 'counties').get(id=aktivitet) context = { 'aktivitet': aktivitet, 'difficulties': Aktivitet.DIFFICULTY_CHOICES, 'audiences': AktivitetAudience.AUDIENCE_CHOICES, 'categories': Aktivitet.CATEGORY_CHOICES, 'all_foreninger': Forening.get_all_sorted(), 'cabins': Cabin.objects.order_by('name'), 'admin_user_search_char_length': settings.ADMIN_USER_SEARCH_CHAR_LENGTH, 'counties': County.typical_objects().order_by('name'), 'municipalities': Municipality.objects.order_by('name'), 'omrader': sorted(Omrade.lookup(), key=lambda o: o.navn), 'now': datetime.now() } return render(request, 'common/admin/aktiviteter/edit/edit.html', context) elif request.method == 'POST': errors = False aktivitet = Aktivitet.objects.get(id=aktivitet) if aktivitet.is_imported(): # Should only be possible by circumventing client-side restrictions return redirect('admin.aktiviteter.views.edit', aktivitet.id) if 'code' in request.POST: aktivitet.code = request.POST['code'] if 'title' in request.POST: aktivitet.title = request.POST['title'] if 'description' in request.POST: aktivitet.description = request.POST['description'] if 'difficulty' in request.POST: aktivitet.difficulty = request.POST['difficulty'] if 'audiences' in request.POST: aktivitet.audiences = [ AktivitetAudience.objects.get(name=audience) for audience in request.POST.getlist('audiences') ] if 'category' in request.POST: aktivitet.category = request.POST['category'] if 'category_type' in request.POST: aktivitet.category_type = request.POST['category_type'] if 'publish' in request.POST: aktivitet.published = request.POST.get('publish') == 'publish' if 'getting_there' in request.POST: aktivitet.getting_there = request.POST['getting_there'] if 'omrader' in request.POST: aktivitet.omrader = request.POST.getlist('omrader') if 'ntb_id' not in request.POST or request.POST['ntb_id'] == '': aktivitet.turforslag = None else: aktivitet.turforslag = request.POST['ntb_id'] if aktivitet.published: # If published, set the extra relevant fields (otherwise ignore them) aktivitet.private = request.POST['private'] == 'private' try: aktivitet.pub_date = datetime.strptime(request.POST['pub_date'], "%d.%m.%Y").date() except ValueError: errors = True messages.error(request, 'invalid_date_format') forening_type, forening_id = request.POST['forening'].split(':') if forening_type == 'forening': forening = Forening.objects.get(id=forening_id) if not forening in request.user.children_foreninger(): raise PermissionDenied aktivitet.forening = forening elif forening_type == 'cabin': aktivitet.forening_cabin = Cabin.objects.get(id=forening_id) else: raise PermissionDenied if 'co_foreninger[]' in request.POST and request.POST['co_foreninger[]'] != '': co_foreninger = [] co_foreninger_cabin = [] for co_forening in request.POST.getlist('co_foreninger[]'): type, id = co_forening.split(':') if type == 'forening': co_foreninger.append(id) elif type == 'cabin': co_foreninger_cabin.append(id) else: raise PermissionDenied aktivitet.co_foreninger = co_foreninger aktivitet.co_foreninger_cabin = co_foreninger_cabin else: aktivitet.co_foreninger = [] aktivitet.co_foreninger_cabin = [] if 'latlng' in request.POST: latlng = request.POST['latlng'].split(',') if len(latlng) == 2: aktivitet.start_point = Point(float(latlng[0]), float(latlng[1])) aktivitet.save() aktivitet.counties = request.POST.getlist('counties') aktivitet.municipalities = request.POST.getlist('municipalities') aktivitet.category_tags.clear() if 'category_tags' in request.POST and request.POST['category_tags'] != '': for tag in request.POST.getlist('category_tags'): obj, created = Tag.objects.get_or_create(name=tag) aktivitet.category_tags.add(obj) aktivitet.images.all().delete() for i, image in parse_html_array(request.POST, 'images').items(): AktivitetImage( aktivitet=aktivitet, url=image['url'], text=image['description'], photographer=image['photographer'], order=i ).save() dates = parse_html_array(request.POST, 'dates').items() # Remove the date objects that were explicitly deleted (checks and verifications are done # client-side). Verify that those that would be implicitly deleted (by not being POSTed for # editing) match those explicitly POSTed. date_ids = [int(d['id']) for k, d in dates if d['id'] != ''] implicit_del = set([date.id for date in aktivitet.dates.all() if date.id not in date_ids]) if len(implicit_del) > 0: # Better to raise an exception and not delete anything. The user will be confused and # lose edits, but we'll get a report and hopefully be able to fix this, if it ever # happens. raise Exception("Implicit delete of AktivitetDate is strictly forbidden!") for i, date in dates: if date['id'] != '': # @TODO Check if this can be exploited. Can you hijack another trip's date by # setting an arbitrary ID in the date['id'] field? model = AktivitetDate.objects.get(id=date['id']) else: model = AktivitetDate(aktivitet=aktivitet) # @TODO for existing dates; if model.start_date > now; dissalow editing. # Explicit delete of dates if date['status'] == 'delete': if date['id'] != '': if model.participant_count() > 0: raise Exception("Date with participants can not be deleted!") model.delete() continue try: if not date['start_time']: date['start_time'] = '08:00' if not date['end_time']: date['end_time'] = '16:00' # @TODO check start_time > now model.start_date = datetime.strptime( "%s %s" % (date['start_date'], date['start_time']), "%d.%m.%Y %H:%M" ) # @TODO check end_time > start_time model.end_date = datetime.strptime( "%s %s" % (date['end_date'], date['end_time']), "%d.%m.%Y %H:%M" ) # @TODO check start_date > meeting_time if date['start_date'] and date['meeting_time']: model.meeting_time = datetime.strptime( "%s %s" % (date['start_date'], date['meeting_time']), "%d.%m.%Y %H:%M" ) if not date['signup_method'] or date['signup_method'] == 'none': # To the next maintainer. This block indicates that a date does not allow # signup. However, keep in mind that this might be an existing date with # participants. Hence, do not set model.participant to None event though it # might be tempting! model.signup_enabled = False model.signup_start = None model.signup_deadline = None model.cancel_deadline = None elif date['signup_method'] == 'normal' or date['signup_method'] == 'simple': model.signup_enabled = True if date.get('max_participants_limited'): model.max_participants = date['max_participants'] else: model.max_participants = None if date.get('no_signup_start') == '1': model.signup_start = None else: model.signup_start = datetime.strptime( date['signup_start'], "%d.%m.%Y", ).date() if 'no_signup_deadline' in date and date['no_signup_deadline'] == '1': model.signup_deadline = None elif 'signup_deadline' in date and date['signup_deadline'] != '': model.signup_deadline = datetime.strptime( date['signup_deadline'], "%d.%m.%Y", ).date() if 'no_cancel_deadline' in date and date['no_cancel_deadline'] == '1': model.cancel_deadline = None elif 'cancel_deadline' in date and date['cancel_deadline'] != '': model.cancel_deadline = datetime.strptime( date['cancel_deadline'], "%d.%m.%Y" ).date() else: raise Exception("Unrecognized POST value for signup_method field") except ValueError: errors = True messages.error(request, 'invalid_date_format') return redirect('admin.aktiviteter.views.edit', aktivitet.id) # Note that simple signup is currently disabled and due to be removed model.signup_simple_allowed = False model.meeting_place = date['meeting_place'] model.contact_type = date['contact_type'] model.contact_custom_name = date['contact_custom_name'] model.contact_custom_phone = date['contact_custom_phone'] model.contact_custom_email = date['contact_custom_email'] model.should_have_turleder = date.get('should_have_turleder') == '1' model.save() if date.get('should_have_turleder') == '1': # We need to specify the key for this particular field because the parse_html_array # function does not properly parse multidimensional arrays. key = 'dates[%s][turleder][]' % i if key in request.POST and request.POST[key] != '': model.turledere = request.POST.getlist(key) else: model.turledere = [] if not errors: messages.info(request, 'save_success') if json.loads(request.POST['preview']): return redirect('admin.aktiviteter.views.preview', aktivitet.id) else: return redirect('admin.aktiviteter.views.edit', aktivitet.id)
def index(request): turledere = cache.get('admin.turleder_count') if turledere is None: turledere = Turleder.objects.only( # Select only the related approved forening, and only the ID from that model 'forening_approved', 'forening_approved__id', ).select_related( # Join the related forening into this query as we'll traverse that later 'forening_approved', ).distinct( # The Turleder model may have >1 references to a user, so select distinctly on user 'user', ) # Make sure the lazy queryset has been evaluated before caching it len(turledere) cache.set('admin.turleder_count', turledere, 60 * 60 * 24 * 14) turleder_stats = { 'total': len(turledere), # Filter on the local forening in code - it's prefetched, so that's much faster, and it lets us cache the # entire query instead of having a seperate cache key per forening 'local': len([t for t in turledere if t.forening_approved == request.active_forening]), } aktiviteter = cache.get('admin.aktivitet_count.%s' % request.active_forening.id) if aktiviteter is None: aktiviteter = AktivitetDate.get_searchable().filter( Q(aktivitet__organizer_forening=request.active_forening) | Q(aktivitet__co_organizers_forening=request.active_forening), start_date__gte=date.today(), ).count() cache.set('admin.aktivitet_count', aktiviteter, 60 * 60 * 6) dashboard_stats = { 'turledere': turleder_stats, 'aktiviteter': aktiviteter, } betablog = cache.get('admin.betablog') BETABLOG_ITEM_COUNT = 4 if betablog is None: try: betablog = [] librato.increment('sherpa.requests.betablog') r = requests.get("http://beta.dnt.no/", params={'feed': 'rss2'}) channel = ET.fromstring(r.content).find('channel') for item in channel.findall('item')[:BETABLOG_ITEM_COUNT]: content = item.find('description').text image = None m = re.search('<img.*?src="(.*?)" ', content) if m is not None: image = m.group(1) pub_date = datetime.strptime(item.find('pubDate').text[:-6], "%a, %d %b %Y %H:%M:%S") betablog.append({ 'title': item.find('title').text, 'link': item.find('link').text, 'content': content, 'image': image, 'pub_date': pub_date, 'is_new': pub_date > datetime.now() - timedelta(days=7), }) except: logger.warning( "Kunne ikke hente innhold fra betabloggen", exc_info=sys.exc_info(), extra={'request': request} ) cache.set('admin.betablog', betablog, 60 * 60 * 12) context = { 'betablog': betablog, 'dashboard_stats': dashboard_stats, } return render(request, 'central/admin/dashboard.html', context)
def index(request): turleder_stats = cache.get('admin.turleder_count') if turleder_stats is None: turledere = Turleder.objects.only( # Select only the related approved forening, and only the ID from that model 'forening_approved', 'forening_approved__id', ).select_related( # Join the related forening into this query as we'll traverse that later 'forening_approved', ).distinct( # The Turleder model may have >1 references to a user, so select distinctly on user 'user', ) turleder_stats = { 'total': len(turledere), 'all': { f.id: len([t for t in turledere if t.forening_approved == f]) for f in Forening.objects.only('id') }, } cache.set('admin.turleder_count', turleder_stats, 60 * 60 * 24 * 14) # Set a shortcut key for the count for the current active forening, outside of cache logic turleder_stats['active_forening'] = turleder_stats['all'].get(request.active_forening.id, 0) aktiviteter = cache.get('admin.aktivitet_count.%s' % request.active_forening.id) if aktiviteter is None: aktiviteter = AktivitetDate.get_searchable().filter( Q(aktivitet__organizer_forening=request.active_forening) | Q(aktivitet__co_organizers_forening=request.active_forening), start_date__gte=date.today(), ).count() cache.set('admin.aktivitet_count.%s' % request.active_forening.id, aktiviteter, 60 * 60 * 6) dashboard_stats = { 'turledere': turleder_stats, 'aktiviteter': aktiviteter, } # Get info on user announcements active_count = UserAnnouncement.objects.filter( # Set to be published before now() Q(pub_date__isnull=True) | Q(pub_date__lte=datetime.now()), # Set to be unpublished after now() Q(unpub_date__isnull=True) | Q(unpub_date__gt=datetime.now()), ).count() upcoming_count = UserAnnouncement.objects.filter( # Set to be unpublished after now() Q(unpub_date__isnull=True) | Q(unpub_date__gt=datetime.now()), # Set to be unpublished before now() pub_date__gt=datetime.now(), ).count() context = { 'user_announcements_active_count': active_count, 'user_announcements_upcoming_count': upcoming_count, 'dashboard_stats': dashboard_stats, } return render(request, 'central/admin/dashboard.html', context)
def parse(self, widget_options, site, preview_context): aktivitet_dates = AktivitetDate.get_searchable().filter( start_date__gte=date.today(), ).select_related( 'aktivitet', 'aktivitet__organizer_forening', ).prefetch_related( 'aktivitet__images', 'aktivitet__co_organizers_forening', ) # # Note that all widget search filters should match the behavior in the standard search view. Not only for # consistency's sake, but also because we'll want to link from the widget to the full search with the same # search parameters repeated; expecting the full search to display the exact same results. # url_query_parameters = {} # Apply audience filters, if any if len(widget_options['audiences']) > 0: # Note the usage of 'overlap' here to combine multiple audiences by 'or' aktivitet_dates = aktivitet_dates.filter(aktivitet__audiences__overlap=widget_options['audiences']) url_query_parameters['audiences'] = ",".join(widget_options['audiences']) # Apply category filters, if any if len(widget_options['categories']) > 0: aktivitet_dates = aktivitet_dates.filter( aktivitet__category__in=widget_options['categories'], ) url_query_parameters['categories'] = ",".join(widget_options['categories']) # Apply category type filters, if any if len(widget_options['category_types']) > 0: # Note that we're checking for both types and tags, and since objects may have the same tag specified twice, # it'll require an explicit distinct clause in our query aktivitet_dates = aktivitet_dates.filter( Q(aktivitet__category_type__in=widget_options['category_types']) | Q(aktivitet__category_tags__name__in=widget_options['category_types']) ) url_query_parameters['category_types'] = ",".join(widget_options['category_types']) # Apply category tag filters, if any if len(widget_options['category_tags']) > 0: aktivitet_dates = aktivitet_dates.filter( aktivitet__category_tags__name__in=widget_options['category_tags'], ) # Note that the full search UI doesn't expose a control for this parameter, so the initial page load will # work, but when one changes any other filter, the category_tags filter will be cleared, which is suboptimal url_query_parameters['category_tags'] = ",".join(widget_options['category_tags']) # Apply organizer filter, if any if len(widget_options['organizers']) > 0: foreninger = [] cabins = [] for organizer in widget_options['organizers']: type, id = organizer.split(':') if type == 'forening': foreninger.append(id) elif type == 'cabin': cabins.append(id) # This will always be true (since we checked organizers length above), but just to be explicit: The filter # should not be executed if both lists are empty. if len(foreninger) > 0 or len(cabins) > 0: aktivitet_dates = aktivitet_dates.filter( Q(aktivitet__organizer_forening__in=foreninger) | Q(aktivitet__co_organizers_forening__in=foreninger) | Q(aktivitet__organizer_cabin__in=cabins) | Q(aktivitet__co_organizers_cabin__overlap=cabins) ) url_query_parameters['organizers'] = ",".join(widget_options['organizers']) limit = widget_options.get('limit', 50) aktivitet_dates = aktivitet_dates.distinct() total = aktivitet_dates.count() more_activities_count = total - limit aktivitet_dates = aktivitet_dates.order_by('start_date')[:limit] see_more_query_params = "&".join([ "%s=%s" % (urlencode(key), urlencode(value)) for key, value in url_query_parameters.items() ]) show_images = widget_options.get('show_images', False) return { 'aktivitet_dates': aktivitet_dates, 'see_more_query_params': see_more_query_params, 'total': total, 'limit': limit, 'more_activities_count': more_activities_count, 'show_images': show_images, }