def test_cached_as(self): counts = {'a': 0} def inc_a(): counts['a'] += 1 return '' qs = Post.objects.all() t = Template(""" {% load cacheops %} {% cached_as qs 0 'a' %}.a{{ a }}{% endcached_as %} {% cached_as qs timeout=60 fragment_name='a' %}.a{{ a }}{% endcached_as %} {% cached_as qs fragment_name='a' timeout=60 %}.a{{ a }}{% endcached_as %} """) s = t.render(Context({'a': inc_a, 'qs': qs})) self.assertEqual(re.sub(r'\s+', '', s), '.a.a.a') self.assertEqual(counts['a'], 1) t.render(Context({'a': inc_a, 'qs': qs})) self.assertEqual(counts['a'], 1) invalidate_model(Post) t.render(Context({'a': inc_a, 'qs': qs})) self.assertEqual(counts['a'], 2)
def test_cached_as(self): counts = {"a": 0} def inc_a(): counts["a"] += 1 return "" qs = Post.objects.all() t = Template( """ {% load cacheops %} {% cached_as qs 0 'a' %}.a{{ a }}{% endcached_as %} {% cached_as qs timeout=60 fragment_name='a' %}.a{{ a }}{% endcached_as %} {% cached_as qs fragment_name='a' timeout=60 %}.a{{ a }}{% endcached_as %} """ ) s = t.render(Context({"a": inc_a, "qs": qs})) self.assertEqual(re.sub(r"\s+", "", s), ".a.a.a") self.assertEqual(counts["a"], 1) t.render(Context({"a": inc_a, "qs": qs})) self.assertEqual(counts["a"], 1) invalidate_model(Post) t.render(Context({"a": inc_a, "qs": qs})) self.assertEqual(counts["a"], 2)
def clear_cache(objects=None, models=None, purge=False): # this is required to refresh the templates content # otherwise after any update the page will not refresh properly try: cache.clear() except Exception as e: # ignore errors logger.error(str(e)) if _is_cacheops_enabled(): from cacheops import invalidate_all, invalidate_model, invalidate_obj if objects: for obj in objects: invalidate_obj(obj) if models: for model in models: get_content_type(model=model) # add it to the models list invalidate_model(model) if purge: try: invalidate_all() except Exception as e: # Fixes: unknown command `FLUSHDB` # In helm charts FLUSHDB and FLUSHALL commands are disabled logger.error(str(e)) clear_cache(models=list(CONTENT_TYPE_CACHE.keys()))
def on_commit(items, init_flag): if init_flag: invalidate_model(BayesDictionary) else: # у запроса aggregate также сбросится кэш при сбрасывании кэша одной записи for instance in items: invalidate_obj(instance)
def post(self,request, *args, **kwargs): # check name is free if Node.objects.filter(parent=request.POST.get('parent'), name=request.POST.get('name')).count() > 0: return HttpResponse("This name already exists", status=400) node = Node(parent_id=request.POST.get('parent'), name=request.POST.get('name')) node.save() if use_cacheops: print 'invalidate' invalidate_model(Node) node = Node.objects.get(pk=node.pk) return HttpResponse(simplejson.dumps({ 'name' : node.name, 'icon' : 'default', 'loaded' : True, 'pk' : node.pk, 'position' : list(node.parent.get_children()).index(node), 'children' : [], 'parent' : node.parent.pk if node.parent is not None else None }), content_type="application/json", status=201)
def edit_block_view(request, block_id): try: block = EighthBlock.objects.get(id=block_id) except EighthBlock.DoesNotExist: raise http.Http404 if request.method == "POST": form = BlockForm(request.POST, instance=block) if form.is_valid(): form.save() invalidate_model(EighthBlock) messages.success(request, "Successfully edited block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") else: form = BlockForm(instance=block) context = { "form": form, "delete_url": reverse("eighth_admin_delete_block", args=[block_id]), "admin_page_title": "Edit Block" } return render(request, "eighth/admin/edit_form.html", context)
def edit_block_view(request, block_id): try: block = EighthBlock.objects.get(id=block_id) except EighthBlock.DoesNotExist: raise http.Http404 if request.method == "POST": form = BlockForm(request.POST, instance=block) if form.is_valid(): form.save() invalidate_model(EighthBlock) messages.success(request, "Successfully edited block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") else: form = BlockForm(instance=block) context = { "form": form, "delete_url": reverse("eighth_admin_delete_block", args=[block_id]), "admin_page_title": "Edit Block", "block_id": block_id, } return render(request, "eighth/admin/edit_form.html", context)
def edit_block_view(request, block_id): try: block = EighthBlock.objects.get(id=block_id) except EighthBlock.DoesNotExist as e: raise http.Http404 from e if request.method == "POST": form = BlockForm(request.POST, instance=block) if form.is_valid(): form.save() invalidate_model(EighthBlock) messages.success(request, "Successfully edited block.") ####### if settings.ENABLE_HYBRID_EIGHTH: messages.warning( request, """Changing the block name here did not change the groups that were assigned to it. If you need to do this, please delete this block and create the correct one.""", ) ####### return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") else: form = BlockForm(instance=block) context = { "form": form, "delete_url": reverse("eighth_admin_delete_block", args=[block_id]), "admin_page_title": "Edit Block", "block_id": block_id, } return render(request, "eighth/admin/edit_form.html", context)
def test_invalidation_signal(self): def set_signal(signal=None, **kwargs): signal_calls.append(kwargs) signal_calls = [] cache_invalidated.connect(set_signal, dispatch_uid=1, weak=False) invalidate_all() invalidate_model(Post) c = Category.objects.create(title='Hey') self.assertEqual(signal_calls, [ { 'sender': None, 'obj_dict': None }, { 'sender': Post, 'obj_dict': None }, { 'sender': Category, 'obj_dict': { 'id': c.pk, 'title': 'Hey' } }, ])
def handle(self, *args, **options): models = self._get_models(args) if options['verbosity']: self.stdout.write(f"Renaturalizing {len(models)} models.") for model, fields in models: for field in fields: target_field = field.target_field naturalize = field.naturalize_function count = 0 # Print the model and field name if options['verbosity']: self.stdout.write( f"{model._meta.label}.{field.target_field} ({field.name})... ", ending='\n' if options['verbosity'] >= 2 else '') self.stdout.flush() # Find all unique values for the field queryset = model.objects.values_list( target_field, flat=True).order_by(target_field).distinct() for value in queryset: naturalized_value = naturalize(value, max_length=field.max_length) if options['verbosity'] >= 2: self.stdout.write(f" {value} -> {naturalized_value}", ending='') self.stdout.flush() # Update each unique field value in bulk changed = model.objects.filter(name=value).update( **{field.name: naturalized_value}) if options['verbosity'] >= 2: self.stdout.write(f" ({changed})") count += changed # Print the total count of alterations for the field if options['verbosity'] >= 2: self.stdout.write( self.style.SUCCESS( f"{count} {model._meta.verbose_name_plural} updated ({queryset.count()} unique values)" )) elif options['verbosity']: self.stdout.write(self.style.SUCCESS(str(count))) # Invalidate cached queries invalidate_model(model) if options['verbosity']: self.stdout.write(self.style.SUCCESS("Done."))
def update(self, **kwargs): # TODO: not sure whether should invalidate first or update first # from cacheops import invalidate_obj # for obj in self.iterator(): # invalidate_obj(obj) # update must behind invalidate obj n = super(CacheableQuerySet, self).update(**kwargs) if self.is_cacheable(): from cacheops import invalidate_model invalidate_model(self.model) return n
def set_read(self, user, filter): """ Set messages as read. """ qs = self.filter( filter, recipient=user, moderation_status=STATUS_ACCEPTED, read_at__isnull=True, ).update(read_at=now()) invalidate_model(Message) return qs
def put(self,request, *args, **kwargs): node = self.get_object() if Node.objects.filter(name=request.PUT.get('name'), parent=node.parent).exclude(pk=node.pk).count() > 0: return HttpResponse("This name already exists", status=400) node.name = request.POST.get('name') node.save() if use_cacheops: invalidate_model(Node) return HttpResponse('')
def trash(self, user, **kwargs): """ Return messages belonging to a user and marked as deleted. """ related = ('sender', 'recipient') filters = ({ 'recipient': user, 'recipient_deleted_at__isnull': False, 'moderation_status': STATUS_ACCEPTED, }, { 'sender': user, 'sender_deleted_at__isnull': False, }) invalidate_model(Message) return self._folder(related, filters, **kwargs)
def test_invalidation_signal(self): def set_signal(signal=None, **kwargs): signal_calls.append(kwargs) signal_calls = [] cache_invalidated.connect(set_signal, dispatch_uid=1, weak=False) invalidate_all() invalidate_model(Post) c = Category.objects.create(title='Hey') self.assertEqual(signal_calls, [ {'sender': None, 'obj_dict': None}, {'sender': Post, 'obj_dict': None}, {'sender': Category, 'obj_dict': {'id': c.pk, 'title': 'Hey'}}, ])
def add_child(self, **kwargs): r""" Adds a child to the node :param \**kwargs: The supplied keyword arguments :type \**kwargs: dict :return: The new child :rtype: ~integreat_cms.cms.models.abstract_tree_node.AbstractTreeNode """ # Adding a child can modify all other nodes via raw sql queries (which are not recognized by cachalot), # so we have to invalidate the whole model manually. invalidate_model(self.__class__) child = super().add_child(**kwargs) invalidate_model(self.__class__) return child
class Command(LabelCommand): help = 'Invalidates cache for entire app, model or particular instance' args = '(all | <app> | <app>.<model> | <app>.<model>.<pk>) +' label = 'app or model or object' def handle_label(self, label, pk=None, **options): if label == 'all': self.handle_all() else: app_n_model = label.split('.') if len(app_n_model) == 1: self.handle_app(app_n_model[0]) elif len(app_n_model) == 2: self.handle_model(*app_n_model) elif len(app_n_model) == 3: self.handle_obj(*app_n_model) else: raise CommandError( 'Wrong model/app name syntax: %s\nType <app_name> or <app_name>.<model_name>' % label) def handle_all(self): redis_conn.flushdb() def handle_app(self, app_name): try: app = get_app(app_name) except ImproperlyConfigured, e: raise CommandError(e) for model in get_models(app, include_auto_created=True): invalidate_model(model)
def clear_local_database(self): """ Deletes all data related to the local database. This can be used to get a fresh start. """ # Unlink main objects from PeeringDB's before emptying the local database Connection.objects.filter(peeringdb_netixlan__isnull=False).update( peeringdb_netixlan=None) IXP.objects.filter(peeringdb_ixlan__isnull=False).update( peeringdb_ixlan=None) # The use of reversed is important to avoid fk issues for model in reversed(list(NAMESPACES.values())): model.objects.all()._raw_delete(using=DEFAULT_DB_ALIAS) invalidate_model(model) Synchronization.objects.all()._raw_delete(using=DEFAULT_DB_ALIAS) invalidate_model(Synchronization)
def delete_block_view(request, block_id): try: block = EighthBlock.objects.get(id=block_id) except EighthBlock.DoesNotExist: raise http.Http404 if request.method == "POST": block.delete() invalidate_model(EighthBlock) messages.success(request, "Successfully deleted block.") return redirect("eighth_admin_dashboard") else: context = {"admin_page_title": "Delete Block", "item_name": str(block), "help_text": "Deleting this block will remove all records " "of it related to eighth period."} return render(request, "eighth/admin/delete_form.html", context)
def test_cached_as(self): inc = make_inc() qs = Post.objects.all() t = Template(""" {% load cacheops %} {% cached_as qs None 'a' %}.{{ inc }}{% endcached_as %} {% cached_as qs timeout=60 fragment_name='a' %}.{{ inc }}{% endcached_as %} {% cached_as qs fragment_name='a' timeout=60 %}.{{ inc }}{% endcached_as %} """) # All the forms are equivalent self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.1.1.1') # Cache works across calls self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.1.1.1') # Post invalidation clears cache invalidate_model(Post) self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.2.2.2')
def add_sibling(self, pos=None, **kwargs): r""" Adds a new node as a sibling to the current node object :param pos: The position of the new sibling :type pos: str :param \**kwargs: The supplied keyword arguments :type \**kwargs: dict :return: The new sibling :rtype: ~integreat_cms.cms.models.abstract_tree_node.AbstractTreeNode """ # Adding a sibling can modify all other nodes via raw sql queries (which are not recognized by cachalot), # so we have to invalidate the whole model manually. invalidate_model(self.__class__) sibling = super().add_sibling(pos=pos, **kwargs) invalidate_model(self.__class__) return sibling
def delete_block_view(request, block_id): try: block = EighthBlock.objects.get(id=block_id) except EighthBlock.DoesNotExist as e: raise http.Http404 from e if request.method == "POST": block.delete() invalidate_model(EighthBlock) messages.success(request, "Successfully deleted block.") return redirect("eighth_admin_dashboard") else: context = { "admin_page_title": "Delete Block", "item_name": str(block), "help_text": "Deleting this block will remove all records " "of it related to eighth period.", } return render(request, "eighth/admin/delete_form.html", context)
def test_cached_as(self): inc = _make_inc() qs = Post.objects.all() t = Template(""" {% load cacheops %} {% cached_as qs 0 'a' %}.{{ inc }}{% endcached_as %} {% cached_as qs timeout=60 fragment_name='a' %}.{{ inc }}{% endcached_as %} {% cached_as qs fragment_name='a' timeout=60 %}.{{ inc }}{% endcached_as %} """) # All the forms are equivalent self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.1.1.1') # Cache works across calls self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.1.1.1') # Post invalidation clears cache invalidate_model(Post) self.assertRendersTo(t, {'inc': inc, 'qs': qs}, '.2.2.2')
def add_items(self, item_model, items): """Adds items in bulk to the index. If we're adding stuff through the `update_index` management command, we'll receive these in chunks of 1000. We're then splitting those chunks into smaller chunks of 100, I think that helps not overload stuff, but it would be good TODO tests to verify this. Args: item_model (db.Model): The model class we're indexing items (list): A list containing a bunch of items to index. Returns: bool: True """ prepared = [] # Ensure we're not indexing something stale from the cache # This also stops redis from overloading during the indexing if USING_CACHEOPS is True: try: invalidate_model(item_model) except Exception: pass # split items into chunks of 100 chunks = [items[x:x + 100] for x in range(0, len(items), 100)] for chunk in chunks: if self.update_strategy == 'delta': chunk = self._check_deltas(chunk) prepared = [] for item in chunk: doc = self._create_document(self.model, item) prepared.append(doc) if self.update_strategy == 'soft' or self.update_strategy == 'delta': self.index.update_documents(prepared) else: self.index.add_documents(prepared) del (chunk) return True
def cisco_eox_populate_product_lc_state_sync_field(): """ Periodic job to populate the lc_state_sync field in the Products, which shows that the product lifecycle data are automatically synchronized against the Cisco EoX API in this case :return: """ try: cis_vendor = Vendor.objects.get(name__istartswith="Cisco") except: # Vendor doesn't exist, no steps required logger.fatal("Vendor \"Cisco Systems\" not found in database, please check your installation") return {"error": "Vendor \"Cisco Systems\" not found in database"} cisco_products = Product.objects.filter(vendor=cis_vendor) if cisco_products.count() != 0: app_config = AppSettings() queries = app_config.get_cisco_eox_api_queries_as_list() # escape the query strings queries = [re.escape(e) for e in queries] # convert the wildcard values queries = [e.replace("\\*", ".*") for e in queries] queries = ["^" + e + "$" for e in queries] with transaction.atomic(): # reset all entries for the vendor Product.objects.filter(vendor=cis_vendor).update(lc_state_sync=False) # only set the state sync to true if the periodic synchronization is enabled if app_config.is_periodic_sync_enabled(): for query in queries: Product.objects.filter(product_id__regex=query, vendor=cis_vendor).update(lc_state_sync=True) invalidate_model(Product) return {"status": "Database updated"} else: return {"error": "No Products associated to \"Cisco Systems\" found in database"}
def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. :param target: The target mode which determines the new position :type target: ~integreat_cms.cms.models.abstract_tree_node.AbstractTreeNode :param pos: The new position of the page relative to the target (choices: :mod:`~integreat_cms.cms.constants.position`) :type pos: str :raises ~treebeard.exceptions.InvalidPosition: If the node is moved to another region """ logger.debug("Moving %r to position %r of %r", self, pos, target) # Do not allow to move a node outside its region if self.region != target.region: # Allow moving as siblings of root nodes (because it's a separate tree) if not (target.is_root() and pos in [ position.LEFT, position.RIGHT, position.FIRST_SIBLING, position.LAST_SIBLING, ]): raise InvalidPosition( _('The node "{}" in region "{}" cannot be moved to "{}".'). format(self, self.region, target.region)) # Moving a node can modify all other nodes via raw sql queries (which are not recognized by cachalot), # so we have to invalidate the whole model manually. invalidate_model(self.__class__) super().move(target=target, pos=pos) invalidate_model(self.__class__) # Reload 'self' because lft/rgt may have changed self.refresh_from_db() # Update parent to fix inconsistencies between tree fields new_parent = self.get_parent(update=True) logger.debug("Updating parent field from %r to %r", self.parent, new_parent) self.parent = new_parent self.save()
def post(self,request, *args, **kwargs): target = Node.objects.get(pk=request.DATA.get('target')); node = Node.objects.get(pk=request.DATA.get('node')) count = target.children.filter(models.Q(name=node.name) | models.Q(name__regex=r'^%s \([0-9]+\)$' % node.name)).count() if count > 0: q = target.children.filter(name__regex=r'^%s \([0-9]+\)$' % node.name) if q.count() > 0: name = q.order_by('-name')[0].name count = int(re.match('.* \(([0-9+])\)', name).group(1)) node.name = node.name + " (%s)" % (count + 1) node.save() # mptt sucks node.parent = target node.save() if use_cacheops: invalidate_model(Node) return HttpResponse(simplejson.dumps({'name' : node.name, 'position' : list(Node.objects.get(pk=request.DATA.get('target')).get_children()).index(node)}), content_type="application/json")
def handle_model(self, app_name, model_name): model = get_model(app_name, model_name) if model is None: raise CommandError('Unknown model: %s.%s' % (app_name, model_name)) invalidate_model(model)
def repair_durations(limit_range, dump_to, load_from, tolerance, log_file): """ Repair/reprocess master durations. """ from base.audio.fileinfo import FileInfoProcessor items_to_reprocess = [] affected_playlists = [] affected_playlist_ids = [] # invalidate cache for Media invalidate_model(Media) if load_from: if limit_range: raise NotImplementedError( '--limit-range option not allowed in combination with --load-from' ) # using `set` to remove duplicate ids item_ids = set([ int(l.strip().split(',')[0]) for l in load_from.readlines() if float(l.strip().split(',')[1]) > tolerance ]) click.echo('loaded {} ids from dump file'.format(len(item_ids))) items_to_reprocess = Media.objects.filter(pk__in=item_ids) else: # mysql does not support remote/streaming cursors # to save memory items are loaded from db individually values = Media.objects.order_by('pk').values('id').nocache() if limit_range: _limits = limit_range.split(':') values = values[_limits[0]:_limits[1]] item_ids = [i['id'] for i in values] with click.progressbar(item_ids, show_pos=True, width=48, label='Reprocessing {} tracks'.format( len(item_ids))) as bar: for item_pk in bar: close_old_connections() item = Media.objects.get(pk=item_pk) if item.master and item.master.path: p = FileInfoProcessor(item.master.path) current_duration = item.master_duration new_duration = p.duration try: diff = abs(current_duration - new_duration) except TypeError: diff = 100.0 if diff > tolerance: items_to_reprocess.append(item) # add to csv log if diff > tolerance and dump_to: dump_to.write('{pk},{diff}\n'.format(pk=item.pk, diff=diff)) dump_to.flush() click.echo('{} tracks have differences in duration'.format( len(items_to_reprocess))) if click.confirm( 'Do you want to update/repair the durations on {} tracks?'.format( len(items_to_reprocess))): base_url = 'http://{}'.format(Site.objects.get_current().domain) tpl = u'''id: {id} - "{name}" {url} old: {current_duration} new: {new_duration} diff: {diff} ''' tpl_log = u'{ct},{pk},{type},{current_duration},{new_duration},{diff},{url}\n' # write column header if log_file: log_file.write( tpl_log.format( ct='content-type', pk='id', url='url', type='type', # current_duration='old_duration', new_duration='new_duration', diff='diff', )) # loop affected media, fix durations, get playlist appearances & print/log info for item in items_to_reprocess: p = FileInfoProcessor(item.master.path) current_duration = item.master_duration new_duration = p.duration try: diff = current_duration - new_duration except TypeError: diff = '-' click.echo( tpl.format( id=item.id, name=item.name, url=base_url + item.get_absolute_url(), # current_duration=current_duration, new_duration=new_duration, diff=diff)) if log_file: log_file.write( tpl_log.format( ct='media', pk=item.pk, url=base_url + item.get_absolute_url(), type=item.get_mediatype_display(), # current_duration=current_duration, new_duration=new_duration, diff=diff)) log_file.flush() for p in item.get_appearances(): if not p.pk in affected_playlist_ids: affected_playlist_ids.append(p.pk) # we need to store the 'current' value of the duration affected_playlists.append({ 'obj': p, 'current_duration': p.get_duration() }) # update media duration Media.objects.filter(pk=item.pk).update( master_duration=new_duration) invalidate_obj(item) # loop playlists & print/log info for item in affected_playlists: invalidate_obj(item['obj']) current_duration = float(item['current_duration']) / 1000 new_duration = float(item['obj'].get_duration()) / 1000 try: diff = current_duration - new_duration except TypeError: diff = '-' click.echo( tpl.format( id=item['obj'].id, name=item['obj'].name, url=base_url + item['obj'].get_absolute_url(), # current_duration=current_duration, new_duration=new_duration, diff=diff)) if log_file: log_file.write( tpl_log.format( ct='playlist', pk=item['obj'].pk, url=base_url + item['obj'].get_absolute_url(), type=item['obj'].get_type_display(), # current_duration=current_duration, new_duration=new_duration, diff=diff)) log_file.flush() # update playlist duration Playlist.objects.filter(pk=item['obj'].pk).update( duration=new_duration * 1000) invalidate_obj(item)
def add_block_view(request): if request.method == "POST" and "custom_block" in request.POST: form = QuickBlockForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Successfully added block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") request.session["add_block_form"] = pickle.dumps(form) date = None show_letters = None if "date" in request.GET: date = request.GET.get("date") if "date" in request.POST: date = request.POST.get("date") title_suffix = "" if date: date_format = re.compile(r"([0-9]{2})\/([0-9]{2})\/([0-9]{4})") fmtdate = date_format.sub(r"\3-\1-\2", date) title_suffix = " - {}".format(fmtdate) show_letters = True if "modify_blocks" in request.POST: letters = request.POST.getlist("blocks") current_letters = [] blocks_day = EighthBlock.objects.filter(date=fmtdate) for day in blocks_day: current_letters.append(day.block_letter) for ltr in letters: if not ltr: continue if ltr not in current_letters: EighthBlock.objects.create(date=fmtdate, block_letter=ltr) messages.success( request, "Successfully added {} Block on {}".format( ltr, fmtdate)) for ltr in current_letters: if not ltr: continue if ltr not in letters: EighthBlock.objects.get(date=fmtdate, block_letter=ltr).delete() messages.success( request, "Successfully removed {} Block on {}".format( ltr, fmtdate)) invalidate_model(EighthBlock) if request.POST.get("assign_withdrawn", "off") == "on": try: grp = Group.objects.get(name="Withdrawn from TJ") act = EighthActivity.objects.get(name="Z - Withdrawn from TJ") for block in EighthBlock.objects.filter(date=fmtdate): sch_act = EighthScheduledActivity.objects.update_or_create( block=block, activity=act, defaults={ "attendance_taken": True, "administrative": True, "sticky": True, "capacity": 100, "restricted": True }, )[0] for u in grp.user_set.all(): sch_act.add_user(u, request=None, force=True) messages.success( request, "Successfully signed withdrawn students up for the withdrawn activity." ) except (Group.DoesNotExist, EighthActivity.DoesNotExist): messages.error( request, 'Unable to assign withdrawn students; either the "Withdrawn from TJ" group or "Z - Withdrawn from TJ" activity does not exist.', ) ####### if settings.ENABLE_HYBRID_EIGHTH: if request.POST.get("assign_hybrid", "off") == "on": blocks = EighthBlock.objects.filter(date=fmtdate) block_names = { b.block_letter[4:] for b in blocks if "P1" in b.block_letter or "P2" in b.block_letter or "Virt" in b.block_letter } if "Virt" in block_names and ("P1" in block_names or "P2" in block_names): eighth_admin_assign_hybrid_sticky_blocks.delay(fmtdate=fmtdate) messages.success(request, "Assigned groups successfully.") elif "Virt" in block_names and len( block_names) == 1: # everyone is virtual messages.warning( request, "Only virtual blocks have been created, so not assigning any groups." ) else: # something is wrong messages.error( request, "Created blocks successfully, but couldn't assign groups. Please do this manually." ) ####### letters = [] visible_blocks = ["A", "B", "C", "D", "E", "F", "G", "H"] ####### if settings.ENABLE_HYBRID_EIGHTH: visible_blocks = [ "A - Virt", "A - P1", "A - P2", "B - Virt", "B - P1", "B - P2" ] ####### if show_letters: onday = EighthBlock.objects.filter(date=fmtdate) for ltr in visible_blocks: exists = onday.filter(block_letter=ltr) letters.append({"name": ltr, "exists": exists}) for blk in onday: if blk.block_letter not in visible_blocks: visible_blocks.append(blk.block_letter) letters.append({"name": blk.block_letter, "exists": True}) context = { "admin_page_title": "Add or Remove Blocks{}".format(title_suffix), "date": date, "letters": letters, "show_letters": show_letters, "add_block_form": QuickBlockForm, } ####### if settings.ENABLE_HYBRID_EIGHTH: context.update({"hybrid": True}) ####### return render(request, "eighth/admin/add_block.html", context)
def add_block_view(request): if request.method == "POST" and "custom_block" in request.POST: form = QuickBlockForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Successfully added block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") request.session["add_block_form"] = pickle.dumps(form) date = None show_letters = None if "date" in request.GET: date = request.GET.get("date") if "date" in request.POST: date = request.POST.get("date") title_suffix = "" if date: date_format = re.compile(r"([0-9]{2})\/([0-9]{2})\/([0-9]{4})") fmtdate = date_format.sub(r"\3-\1-\2", date) logger.debug(fmtdate) title_suffix = " - {}".format(fmtdate) show_letters = True if "modify_blocks" in request.POST: letters = request.POST.getlist("blocks") current_letters = [] blocks_day = EighthBlock.objects.filter(date=fmtdate) for day in blocks_day: current_letters.append(day.block_letter) logger.debug(letters) logger.debug(current_letters) for l in letters: if not l: continue if l not in current_letters: EighthBlock.objects.create(date=fmtdate, block_letter=l) messages.success( request, "Successfully added {} Block on {}".format(l, fmtdate)) for l in current_letters: if not l: continue if l not in letters: EighthBlock.objects.get(date=fmtdate, block_letter=l).delete() messages.success( request, "Successfully removed {} Block on {}".format( l, fmtdate)) invalidate_model(EighthBlock) letters = [] visible_blocks = ["A", "B", "C", "D", "E", "F", "G", "H"] if show_letters: onday = EighthBlock.objects.filter(date=fmtdate) for l in visible_blocks: exists = onday.filter(block_letter=l) letters.append({"name": l, "exists": exists}) for blk in onday: if blk.block_letter not in visible_blocks: visible_blocks.append(blk.block_letter) letters.append({"name": blk.block_letter, "exists": True}) context = { "admin_page_title": "Add or Remove Blocks{}".format(title_suffix), "date": date, "letters": letters, "show_letters": show_letters, "add_block_form": QuickBlockForm, } return render(request, "eighth/admin/add_block.html", context)
def edit_group_view(request, group_id): try: group = Group.objects.get(id=group_id) except Group.DoesNotExist: raise http.Http404 if request.method == "POST": invalidate_model(Group) if group.name.lower().startswith("all students"): cache.delete("users:students") if "remove_all" in request.POST: users = group.user_set.all() num = users.count() for u in users: group.user_set.remove(u) group.save() invalidate_obj(group) messages.success(request, "Successfully deleted {} members of the group.".format(num)) return redirect("eighth_admin_edit_group", group.id) form = GroupForm(request.POST, instance=group) if form.is_valid(): if 'student_visible' in form.cleaned_data: props = group.properties props.student_visible = form.cleaned_data['student_visible'] props.save() invalidate_obj(props) form.save() messages.success(request, "Successfully edited group.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error modifying group.") else: form = GroupForm(instance=group, initial={"student_visible": group.properties.student_visible}) student_query = None if request.method == "GET": student_query = request.GET.get("q", None) if not student_query: users = group.user_set.all() # Order not strictly alphabetical else: ion_ids = [sid.strip() for sid in student_query.split(",")] users = group.user_set.filter(username__in=ion_ids) users = users.order_by('username', 'first_name', 'last_name', 'student_id') p = Paginator(users, 100) # Paginating to limit LDAP queries (slow) page_num = request.GET.get('p', 1) try: page = p.page(page_num) except PageNotAnInteger: page = p.page(1) except EmptyPage: page = p.page(p.num_pages) members = [] for user in page: grade = user.grade emails = user.emails members.append({ "id": user.id, "first_name": user.first_name, "last_name": user.last_name, "student_id": user.student_id, "email": user.tj_email if user.tj_email else emails.first() if emails.count() > 0 else "", "grade": grade.number if user.grade and not user.grade.number == 13 else "Staff" }) members = sorted(members, key=lambda m: (m["last_name"], m["first_name"])) linked_activities = EighthActivity.objects.filter(groups_allowed=group) def parse_int(value): return int(value) if value.isdigit() else None context = { "group": group, "members": members, "member_count": users.count(), "members_page": page, "edit_form": form, "added_ids": [parse_int(x) for x in request.GET.getlist("added")], "linked_activities": linked_activities, "admin_page_title": "Edit Group", "delete_url": reverse("eighth_admin_delete_group", args=[group_id]) } if "possible_student" in request.GET: student_ids = request.GET.getlist("possible_student") possible_students = User.objects.get(id__in=student_ids) context["possible_students"] = possible_students return render(request, "eighth/admin/edit_group.html", context)
def edit_group_view(request, group_id): try: group = Group.objects.get(id=group_id) except Group.DoesNotExist: raise http.Http404 if request.method == "POST": invalidate_model(Group) if group.name.lower().startswith("all students"): cache.delete("users:students") if "remove_all" in request.POST: users = group.user_set.all() num = users.count() for u in users: group.user_set.remove(u) group.save() invalidate_obj(group) messages.success( request, "Successfully deleted {} members of the group.".format(num)) return redirect("eighth_admin_edit_group", group.id) form = GroupForm(request.POST, instance=group) if form.is_valid(): if 'student_visible' in form.cleaned_data: props = group.properties props.student_visible = form.cleaned_data['student_visible'] props.save() invalidate_obj(props) form.save() messages.success(request, "Successfully edited group.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error modifying group.") else: form = GroupForm( instance=group, initial={"student_visible": group.properties.student_visible}) student_query = None if request.method == "GET": student_query = request.GET.get("q", None) if not student_query: users = group.user_set.all() # Order not strictly alphabetical else: ion_ids = [sid.strip() for sid in student_query.split(",")] users = group.user_set.filter(username__in=ion_ids) p = Paginator(users, 100) # Paginating to limit LDAP queries (slow) page_num = request.GET.get('p', 1) try: page = p.page(page_num) except PageNotAnInteger: page = p.page(1) except EmptyPage: page = p.page(p.num_pages) members = [] for user in page: grade = user.grade emails = user.emails members.append({ "id": user.id, "first_name": user.first_name, "last_name": user.last_name, "student_id": user.student_id, "email": user.tj_email if user.tj_email else emails[0] if emails else "", "grade": grade.number if user.grade and not user.grade.number == 13 else "Staff" }) members = sorted(members, key=lambda m: (m["last_name"], m["first_name"])) linked_activities = EighthActivity.objects.filter(groups_allowed=group) def parse_int(value): return int(value) if value.isdigit() else None context = { "group": group, "members": members, "member_count": users.count(), "members_page": page, "edit_form": form, "added_ids": [parse_int(x) for x in request.GET.getlist("added")], "linked_activities": linked_activities, "admin_page_title": "Edit Group", "delete_url": reverse("eighth_admin_delete_group", args=[group_id]) } if "possible_student" in request.GET: student_ids = request.GET.getlist("possible_student") possible_students = User.objects.get(id__in=student_ids) context["possible_students"] = possible_students return render(request, "eighth/admin/edit_group.html", context)
def do_invalidate_model(obj): invalidate_model(obj.__class__)
def add_block_view(request): if request.method == "POST" and "custom_block" in request.POST: form = QuickBlockForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Successfully added block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") request.session["add_block_form"] = pickle.dumps(form) date = None show_letters = None if "date" in request.GET: date = request.GET.get("date") if "date" in request.POST: date = request.POST.get("date") title_suffix = "" if date: date_format = re.compile(r"([0-9]{2})\/([0-9]{2})\/([0-9]{4})") fmtdate = date_format.sub(r"\3-\1-\2", date) title_suffix = " - {}".format(fmtdate) show_letters = True if "modify_blocks" in request.POST: letters = request.POST.getlist("blocks") current_letters = [] blocks_day = EighthBlock.objects.filter(date=fmtdate) for day in blocks_day: current_letters.append(day.block_letter) for ltr in letters: if not ltr: continue if ltr not in current_letters: EighthBlock.objects.create(date=fmtdate, block_letter=ltr) messages.success( request, "Successfully added {} Block on {}".format( ltr, fmtdate)) for ltr in current_letters: if not ltr: continue if ltr not in letters: EighthBlock.objects.get(date=fmtdate, block_letter=ltr).delete() messages.success( request, "Successfully removed {} Block on {}".format( ltr, fmtdate)) invalidate_model(EighthBlock) ####### if settings.ENABLE_HYBRID_EIGHTH: if request.POST.get("assign_hybrid", "off") == "on": blocks = EighthBlock.objects.filter(date=fmtdate) block_names = { b.block_letter[4:] for b in blocks if "P1" in b.block_letter or "P2" in b.block_letter or "Virt" in b.block_letter } if "Virt" in block_names and ("P1" in block_names or "P2" in block_names): eighth_admin_assign_hybrid_sticky_blocks.delay(fmtdate=fmtdate) messages.success(request, "Assigned groups successfully.") elif "Virt" in block_names and len( block_names) == 1: # everyone is virtual messages.warning( request, "Only virtual blocks have been created, so not assigning any groups." ) else: # something is wrong messages.error( request, "Created blocks successfully, but couldn't assign groups. Please do this manually." ) ####### letters = [] visible_blocks = ["A", "B", "C", "D", "E", "F", "G", "H"] ####### if settings.ENABLE_HYBRID_EIGHTH: visible_blocks = [ "A - Virt", "A - P1", "A - P2", "B - Virt", "B - P1", "B - P2" ] ####### if show_letters: onday = EighthBlock.objects.filter(date=fmtdate) for ltr in visible_blocks: exists = onday.filter(block_letter=ltr) letters.append({"name": ltr, "exists": exists}) for blk in onday: if blk.block_letter not in visible_blocks: visible_blocks.append(blk.block_letter) letters.append({"name": blk.block_letter, "exists": True}) context = { "admin_page_title": "Add or Remove Blocks{}".format(title_suffix), "date": date, "letters": letters, "show_letters": show_letters, "add_block_form": QuickBlockForm, } ####### if settings.ENABLE_HYBRID_EIGHTH: context.update({"hybrid": True}) ####### return render(request, "eighth/admin/add_block.html", context)
def save(self, **kwargs): super().save(**kwargs) invalidate_model(ResultsTableEntry)
def save(self, **kwargs): super().save(**kwargs) invalidate_model(BlockData)
def add_block_view(request): if request.method == "POST" and "custom_block" in request.POST: form = QuickBlockForm(request.POST) if form.is_valid(): form.save() messages.success(request, "Successfully added block.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error adding block.") request.session["add_block_form"] = pickle.dumps(form) date = None show_letters = None if "date" in request.GET: date = request.GET.get("date") if "date" in request.POST: date = request.POST.get("date") title_suffix = "" if date: date_format = re.compile(r'([0-9]{2})\/([0-9]{2})\/([0-9]{4})') fmtdate = date_format.sub(r'\3-\1-\2', date) logger.debug(fmtdate) title_suffix = " - {}".format(fmtdate) show_letters = True if "modify_blocks" in request.POST: letters = request.POST.getlist("blocks") current_letters = [] blocks_day = EighthBlock.objects.filter(date=fmtdate) for day in blocks_day: current_letters.append(day.block_letter) logger.debug(letters) logger.debug(current_letters) for l in letters: if len(l) == 0: continue if l not in current_letters: EighthBlock.objects.create(date=fmtdate, block_letter=l) messages.success(request, "Successfully added {} Block on {}".format(l, fmtdate)) for l in current_letters: if len(l) == 0: continue if l not in letters: EighthBlock.objects.get(date=fmtdate, block_letter=l).delete() messages.success(request, "Successfully removed {} Block on {}".format(l, fmtdate)) invalidate_model(EighthBlock) letters = [] visible_blocks = ["A", "B", "C", "D", "E", "F", "G", "H"] if show_letters: onday = EighthBlock.objects.filter(date=fmtdate) for l in visible_blocks: exists = onday.filter(block_letter=l) letters.append({ "name": l, "exists": exists }) for blk in onday: if blk.block_letter not in visible_blocks: visible_blocks.append(blk.block_letter) letters.append({ "name": blk.block_letter, "exists": True }) context = { "admin_page_title": "Add or Remove Blocks{}".format(title_suffix), "date": date, "letters": letters, "show_letters": show_letters, "add_block_form": QuickBlockForm } return render(request, "eighth/admin/add_block.html", context)
def repair_durations(limit_range, dump_to, load_from, tolerance, log_file): """ Repair/reprocess master durations. """ from base.audio.fileinfo import FileInfoProcessor items_to_reprocess = [] affected_playlists = [] affected_playlist_ids = [] # invalidate cache for Media invalidate_model(Media) if load_from: if limit_range: raise NotImplementedError('--limit-range option not allowed in combination with --load-from') # using `set` to remove duplicate ids item_ids = set([ int(l.strip().split(',')[0]) for l in load_from.readlines() if float(l.strip().split(',')[1]) > tolerance ]) click.echo('loaded {} ids from dump file'.format(len(item_ids))) items_to_reprocess = Media.objects.filter(pk__in=item_ids) else: # mysql does not support remote/streaming cursors # to save memory items are loaded from db individually values = Media.objects.order_by('pk').values('id').nocache() if limit_range: _limits = limit_range.split(':') values = values[_limits[0]:_limits[1]] item_ids = [i['id'] for i in values] with click.progressbar(item_ids, show_pos=True, width=48, label='Reprocessing {} tracks'.format(len(item_ids))) as bar: for item_pk in bar: close_old_connections() item = Media.objects.get(pk=item_pk) if item.master and item.master.path: p = FileInfoProcessor(item.master.path) current_duration = item.master_duration new_duration = p.duration try: diff = abs(current_duration - new_duration) except TypeError: diff = 100.0 if diff > tolerance: items_to_reprocess.append(item) # add to csv log if diff > tolerance and dump_to: dump_to.write('{pk},{diff}\n'.format(pk=item.pk, diff=diff)) dump_to.flush() click.echo('{} tracks have differences in duration'.format(len(items_to_reprocess))) if click.confirm('Do you want to update/repair the durations on {} tracks?'.format(len(items_to_reprocess))): base_url = 'http://{}'.format(Site.objects.get_current().domain) tpl = u'''id: {id} - "{name}" {url} old: {current_duration} new: {new_duration} diff: {diff} ''' tpl_log = u'{ct},{pk},{type},{current_duration},{new_duration},{diff},{url}\n' # write column header if log_file: log_file.write(tpl_log.format( ct='content-type', pk='id', url='url', type='type', # current_duration='old_duration', new_duration='new_duration', diff='diff', )) # loop affected media, fix durations, get playlist appearances & print/log info for item in items_to_reprocess: p = FileInfoProcessor(item.master.path) current_duration = item.master_duration new_duration = p.duration try: diff = current_duration - new_duration except TypeError: diff = '-' click.echo(tpl.format( id=item.id, name=item.name, url=base_url + item.get_absolute_url(), # current_duration=current_duration, new_duration=new_duration, diff=diff )) if log_file: log_file.write(tpl_log.format( ct='media', pk=item.pk, url=base_url + item.get_absolute_url(), type=item.get_mediatype_display(), # current_duration=current_duration, new_duration=new_duration, diff=diff )) log_file.flush() for p in item.get_appearances(): if not p.pk in affected_playlist_ids: affected_playlist_ids.append(p.pk) # we need to store the 'current' value of the duration affected_playlists.append({ 'obj': p, 'current_duration': p.get_duration() }) # update media duration Media.objects.filter(pk=item.pk).update(master_duration=new_duration) invalidate_obj(item) # loop playlists & print/log info for item in affected_playlists: invalidate_obj(item['obj']) current_duration = float(item['current_duration']) / 1000 new_duration = float(item['obj'].get_duration()) / 1000 try: diff = current_duration - new_duration except TypeError: diff = '-' click.echo(tpl.format( id=item['obj'].id, name=item['obj'].name, url=base_url + item['obj'].get_absolute_url(), # current_duration=current_duration, new_duration=new_duration, diff=diff )) if log_file: log_file.write(tpl_log.format( ct='playlist', pk=item['obj'].pk, url=base_url + item['obj'].get_absolute_url(), type=item['obj'].get_type_display(), # current_duration=current_duration, new_duration=new_duration, diff=diff )) log_file.flush() # update playlist duration Playlist.objects.filter(pk=item['obj'].pk).update(duration=new_duration * 1000) invalidate_obj(item)
def edit_group_view(request, group_id): try: group = Group.objects.get(id=group_id) except Group.DoesNotExist: raise http.Http404 if request.method == "POST": invalidate_model(Group) if group.name.lower().startswith("all students"): cache.delete("users:students") if "remove_all" in request.POST: users = group.user_set.all() num = users.count() for u in users: group.user_set.remove(u) group.save() invalidate_obj(group) messages.success(request, "Successfully deleted {} members of the group.".format(num)) return redirect("eighth_admin_edit_group", group.id) form = GroupForm(request.POST, instance=group) if form.is_valid(): if 'student_visible' in form.cleaned_data: props = group.properties props.student_visible = form.cleaned_data['student_visible'] props.save() invalidate_obj(props) form.save() messages.success(request, "Successfully edited group.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error modifying group.") else: form = GroupForm(instance=group, initial={"student_visible": group.properties.student_visible}) users = group.user_set.all() members = [] for user in users: grade = user.grade emails = user.emails members.append({ "id": user.id, "first_name": user.first_name, "last_name": user.last_name, "student_id": user.student_id, "email": user.tj_email if user.tj_email else emails[0] if emails else "", "grade": grade.number if user.grade else "Staff" }) members = sorted(members, key=lambda m: (m["last_name"], m["first_name"])) linked_activities = EighthActivity.objects.filter(groups_allowed=group) context = { "group": group, "members": members, "edit_form": form, "added_ids": request.GET.getlist("added"), "linked_activities": linked_activities, "admin_page_title": "Edit Group", "delete_url": reverse("eighth_admin_delete_group", args=[group_id]) } if "possible_student" in request.GET: student_ids = request.GET.getlist("possible_student") possible_students = User.objects.get(id__in=student_ids) context["possible_students"] = possible_students return render(request, "eighth/admin/edit_group.html", context)
def edit_group_view(request, group_id): try: group = Group.objects.get(id=group_id) except Group.DoesNotExist: raise http.Http404 if request.method == "POST": invalidate_model(Group) if group.name.lower().startswith("all students"): cache.delete("users:students") if "remove_all" in request.POST: users = group.user_set.all() num = users.count() for u in users: group.user_set.remove(u) group.save() invalidate_obj(group) messages.success( request, "Successfully deleted {} members of the group.".format(num)) return redirect("eighth_admin_edit_group", group.id) form = GroupForm(request.POST, instance=group) if form.is_valid(): if 'student_visible' in form.cleaned_data: props = group.properties props.student_visible = form.cleaned_data['student_visible'] props.save() invalidate_obj(props) form.save() messages.success(request, "Successfully edited group.") return redirect("eighth_admin_dashboard") else: messages.error(request, "Error modifying group.") else: form = GroupForm( instance=group, initial={"student_visible": group.properties.student_visible}) users = group.user_set.all() members = [] for user in users: grade = user.grade emails = user.emails members.append({ "id": user.id, "first_name": user.first_name, "last_name": user.last_name, "student_id": user.student_id, "email": user.tj_email if user.tj_email else emails[0] if emails else "", "grade": grade.number if user.grade else "Staff" }) members = sorted(members, key=lambda m: (m["last_name"], m["first_name"])) linked_activities = EighthActivity.objects.filter(groups_allowed=group) context = { "group": group, "members": members, "edit_form": form, "added_ids": request.GET.getlist("added"), "linked_activities": linked_activities, "admin_page_title": "Edit Group", "delete_url": reverse("eighth_admin_delete_group", args=[group_id]) } if "possible_student" in request.GET: student_ids = request.GET.getlist("possible_student") possible_students = User.objects.get(id__in=student_ids) context["possible_students"] = possible_students return render(request, "eighth/admin/edit_group.html", context)
def save(self, **kwargs): super().save(**kwargs) invalidate_model(Participant)
def save(self, **kwargs): # if the object does not have the id, then we are # creating and not saving, thus we hash the token if not self.id: logger.info(f"creating {self.__repr__()}") raw_token, token = self.generate_token() self._token = raw_token self.token = token super().save(**kwargs) task_name = f'generating_results_for_raffle_{self.id}' task = PeriodicTask.objects.filter(name=task_name).first() if self.draw_datetime and not self.finalized: schedule, _ = IntervalSchedule.objects.get_or_create(every=3, period=IntervalSchedule.SECONDS) if not task: task = PeriodicTask( name=task_name, interval=schedule, task="core.tasks.generate_raffle_results_task", args=json.dumps([self.id]), ) if self.draw_datetime > timezone.now(): task.start_time = self.draw_datetime else: task.start_time = timezone.now() task.enabled = self.published task.save() # Notifications from notifications.models import NOTIFICATION_TYPE now = timezone.now() task, _ = PeriodicTask.objects.get_or_create( name=f'program_notification_on_raffle_{self.id}_type_{NOTIFICATION_TYPE.ONE_HOUR}', interval=schedule, one_off=True, task="notifications.tasks.send_one_hour_raffle_notifications", args=json.dumps([self.id]), ) task.enabled = True task.start_time = self.draw_datetime - timedelta(hours=1) if task.start_time < now or not self.published: task.enabled = False task.save() task, _ = PeriodicTask.objects.get_or_create( name=f'program_notification_on_raffle_{self.id}_type_{NOTIFICATION_TYPE.ONE_MINUTE}', interval=schedule, one_off=True, task="notifications.tasks.send_one_minute_raffle_notifications", args=json.dumps([self.id]), ) task.start_time = self.draw_datetime - timedelta(minutes=1) task.enabled = True if task.start_time < now or not self.published: task.enabled = False task.save() task, _ = PeriodicTask.objects.get_or_create( name=f'program_notification_on_raffle_{self.id}_type_{NOTIFICATION_TYPE.HAS_STARTED}', interval=schedule, one_off=True, task="notifications.tasks.send_has_started_raffle_notifications", args=json.dumps([self.id]), ) if self.draw_datetime > timezone.now(): task.start_time = self.draw_datetime else: task.start_time = timezone.now() task.enabled = self.published task.save() elif task: if task: task.enabled = False task.save() ResultsTable.objects.get_or_create(raffle=self) invalidate_model(Raffle)
def invalidated_update(qs, **kwargs): n = qs.update(**kwargs) invalidate_model(qs.model) return n