def create_module(self, path, name, shortname): """ Creates a module in the database. Will not create Things above module level, and will fail if the path is not unique. """ # find the parent (remove end slash) try: parent = self.thing_list.get(pathid=Thing.hash(path[:-1])) except Thing.DoesNotExist: self.logger.log( 'failed', 'module', 'Could not find path {0}'.format(path[:-1]) ) return except Thing.MultipleObjectsReturned: self.logger.log( 'failed', 'module', 'Path {0} was not unique'.format(path[:-1]) ) return db_module = Thing( fullname=name, type='module', parent=parent, name=shortname ) self.logger.log('insert', 'module', name) db_module.save() return db_module
def get(self, request, thing, depth="0"): if not request.user.has_perm( Thing.PERM_READ, ThingSubject(fullpath=thing, depth=depth)): return HttpResponseForbidden("Denied") hashid = Thing.hash(thing) try: if depth == "0": thing = Thing.objects.get(pathid=hashid) typeofthing = thing.type if typeofthing is None: typeofthing = "default" context = {"thing": thing} try: return render(request, "student/things/thing-%s.html" % typeofthing, context) except: return render(request, "student/things/thing-default.html", context) else: depth = int(depth) if depth > 10 or depth < 0: return HttpResponseBadRequest( "Sorry no more than 10 levels allowed") things = Thing.objects.filter( Thing.treequery([thing], max_depth=depth)).order_by("fullname") return render(request, "student/list-of-things.html", {"things": things}) except Thing.DoesNotExist: return HttpResponseNotFound()
def test_xml_import_permission(self): logger = APILogger() # read the file file_full_path = os.path.join(xml_path, 'test_add.xml') with open(file_full_path) as opened_file: content = opened_file.read() # process the XML processor = processors.XMLImportProcessor(logger=logger) modules = processor.process(content) importer = importers.APIImporter(self.user, logger=logger) # try importing before the part is made for module in modules: importer.add_module_data(module) if logger.was_success(): self.fail("Imported to non-existent part") logger.clear() # make the part now self.part = Thing( fullname='test', type='part', parent=self.tripos, name='test' ) self.part.save() # try importing with no permission for module in modules: importer.add_module_data(module) if logger.was_success(): self.fail("Imported without permission") else: if logger.failed: self.fail("Failed to import data") logger.clear() # now give permissions and try again tag = ThingTag( thing=self.userthing, targetthing=self.part, annotation="admin" ) tag.save() for module in modules: importer.add_module_data(module) if not logger.was_success(): self.fail("Import failed "+logger.summary())
def get(self, request): site_url = get_site_url_from_request(request) grouped_subjects = Subjects.group_for_part_drill_down( Subjects.all_subjects()) # Get the cambridge year data for the acadamic year set in the settings. acadamic_year = AcademicYear.for_year(settings.DEFAULT_ACADEMIC_YEAR) context = { "subjects": Subjects.to_json(grouped_subjects), "site_url": site_url, "terms": acadamic_year.get_terms_json(), "calendar_start": acadamic_year.start_boundary.isoformat(), "calendar_end": acadamic_year.end_boundary.isoformat() } if request.user.is_authenticated(): # create a url with a hmac in it if the thing is a user. If not just a simple url will do. thing = Thing.get_or_create_user_thing(request.user) context["user_thing"] = thing thingsubject = ThingSubject(thing=thing) hmac = thingsubject.create_hmac() context["ics_feed_url"] = reverse("export ics hmac", kwargs={ "thing": thing.fullpath, "hmac": hmac }) else: context["ics_feed_url"] = None try: context['raven_url'] = settings.RAVEN_URL except: pass return render(request, "student/base.html", context)
def get(self, request, thing, depth="0"): if not request.user.has_perm( Thing.PERM_READ, ThingSubject(fullpath=thing, depth=depth)): return HttpResponseForbidden("Denied") hashid = Thing.hash(thing) try: thing = Thing.objects.get(pathid=hashid) # create a url with a hmac in it if the thing is a user. If not just a simple url will do. thingsubject = ThingSubject(thing=thing) if thingsubject.is_user(): hmac = thingsubject.create_hmac() ics_feed_url = reverse("export ics hmac", kwargs={ "thing": thing.fullpath, "hmac": hmac }) else: ics_feed_url = reverse("export ics", kwargs={"thing": thing.fullpath}) context = { "thing": Thing.objects.get(pathid=hashid), "ics_feed_url": ics_feed_url } return render(request, "calendar.html", context) except Thing.DoesNotExist: return HttpResponseNotFound()
def get_user_thing(self, request, thing): if (request.user.is_authenticated() and self.get_user_permission(request, thing)): return Thing.objects.get(pathid=Thing.hash(thing)) elif thing == self.public_user_path: return None raise PermissionDenied
def post(self, request, thing): user = ThingSubject(fullpath=thing) # Check if the user is logged in if request.user.is_anonymous(): return HttpResponseForbidden("Not logged in") elif not request.user.has_perm(Thing.PERM_LINK, user): return HttpResponseForbidden("Not your calendar") hashid = Thing.hash(thing) # Check if the thing exists try: thing = Thing.objects.get(pathid=hashid) except Thing.DoesNotExist: return HttpResponseNotFound() site_url = get_site_url_from_request(request) new_hmac = user.create_hmac(True) new_feed_path = reverse("export ics hmac", kwargs={ "thing": thing, "hmac": new_hmac }) return HttpResponse(site_url + new_feed_path)
def _get_subject_perms(self, user_obj, obj): if obj.hmac is not None: # A hmac can only ever grant read if obj.is_hmac_valid(): return self.JUST_READ return set() if "user/%s" % user_obj.username == obj.path: return self.ALL try: # Get the thing associated with this user userthing = Thing.objects.get(pathid=Thing.hash("user/%s" % user_obj.username)) # Check if this event is associated with an admin annotation via eventtag or eventsourcetag t = obj.thing if t is not None: Thing.objects.get(id=t.id, relatedthing__thing=userthing, eventtag__annotation="admin") return self.ALL except Thing.DoesNotExist: # One or more of the things we were looking for doesn't exist, therefore therefore there is only read. pass # If we got this far, and this is a user Thing then read should be denied if obj.is_user(): return set() # could do something more sophisticated here, but just at the moment there is no need. return self.JUST_READ
def get(self, request, thing, hmac=None): if not request.user.has_perm(Thing.PERM_READ, ThingSubject(fullpath=thing, hmac=hmac)): return HttpResponseForbidden("Denied") hashid = Thing.hash(thing) outputformat = request.path.split(".")[-1] try: thing = Thing.objects.get(pathid=hashid) if outputformat in settings.EVENT_EXPORTERS: exporter_class = settings.EVENT_EXPORTERS[outputformat] exporter = newinstance(exporter_class) if exporter is not None: depth = self.get_depth() print "Depth: {}".format(depth) events = (thing.get_events(depth=depth) # The series is referenced from event in order to use # the series title in the output, so it helps to # prefetch the series to avoid len(events) queries # when iterating over events. .prefetch_related("source")) return exporter.export(events, feed_name=self._path_to_filename(thing.fullpath)) return HttpResponseBadRequest("Sorry, Format not recognized, can't load class %s " % exporter_class ) return HttpResponseBadRequest("Sorry, Format not recognized") except Thing.DoesNotExist: return HttpResponseNotFound()
def __get_thing(self): try: path = self.get_thing_fullpath() return Thing.objects.get(pathid=Thing.hash(path)) except Thing.DoesNotExist: # Raise permission denied instead of 404 when a Thing does # not exist to avoid leaking presence of a user... raise PermissionDenied
def process_module_dict(self, module): """ Processes a ModuleData dict and updates the database """ path = module['path'] try: db_module = self.thing_list.get( pathid=Thing.hash(path+module['shortname']) ) except Thing.DoesNotExist: db_module = None is_deleting_module = module.is_being_deleted() if db_module is None: if is_deleting_module: # wanted to delete it, doesn't exist, nothing to do return db_module = self.create_module( path, module['name'], module['shortname'] ) if db_module is None: # something went wrong creating the module (no need to report # it as the logger should already contain the details) return # check if we want to delete it if is_deleting_module: self.delete_module(db_module) return # create a list of child sources module_sources = [] matching_source_tags = self.event_source_list.filter( thing=db_module, annotation='home' ) for tag in matching_source_tags: # check it was imported via the api if 'importid' in tag.eventsource.metadata: module_sources.append( (tag.eventsource, tag.eventsource.metadata['importid']) ) for source in module['seriesList']: db_source = self.process_source_dict( module_sources, db_module, source ) if db_source is not None: module_sources.append( (db_source, db_source.metadata['importid']) )
def _get_subject_perms(self, user_obj, obj): try: # Is there anything associated with the user userthing = Thing.objects.get(pathid=Thing.hash("user/%s" % user_obj.username)) if ThingTag.objects.filter(thing=userthing, annotation="admin").count() > 0: return self.ALL except Thing.DoesNotExist: pass return self.JUST_READ
def create_salt(self, update=False): ''' Create or update salt in the Thing metadata. Separated from create_hmac so that user salts may be regenerated independently. ''' t = self._get_thing() metadata = t.metadata if update or 'salt' not in metadata: metadata['salt'] = Thing.hash(now().isoformat()) t.save() return True return False
def userHasAccess(user, path): # can this user admin the Thing with that full path # this is set in the users page up to the level before module path = re.sub('/$', '', path) # remove end slash try: thing = Thing.objects.get(pathid=Thing.hash(path)) except Thing.DoesNotExist: return False except Thing.MultipleObjectsReturned: return False return thing.can_be_edited_by(user.username)
def _get_subject_perms(self, user_obj, obj): try: # Get the thing associated with this user userthing = Thing.objects.get(pathid=Thing.hash("user/%s" % user_obj.username)) # Check if this eventsource is associated with an admin annotation via eventsourcetag es = obj.event_source if es is not None: EventSource.objects.get(id=es.id, eventsourcetag__thing=userthing, eventsourcetag__annotation="admin") return self.ALL except Thing.DoesNotExist: pass except EventSource.DoesNotExist: pass return self.JUST_READ
def _get_subject_perms(self, user_obj, obj): # This is a little more complex, we need to find out if the users Thing is associated with # the event. try: # Get the thing associated with this user userthing = Thing.objects.get(pathid=Thing.hash("user/%s" % user_obj.username)) # Check if this event is associated with an admin annotation via eventtag or eventsourcetag e = obj.event if e is not None: Event.objects.filter(id=e.id).get( models.Q(eventtag__thing=userthing, eventtag__annotation="admin") | models.Q(source__eventsourcetag__thing=userthing, source__eventsourcetag__annotation="admin")) return self.ALL except Thing.DoesNotExist: pass except Event.DoesNotExist: pass return self.JUST_READ
def add_module_data(self, module): """ Adds the data in module to the database. Performs a check to ensure that user has the appropriate permission. Will not create Things above the module level. """ try: module.is_valid() except DataValidationException as err: self.logger.log( 'failed', 'module', 'Module data not valid {0}'.format(err) ) return path = module['path'] # find the parent of the module try: parent_thing = self.thing_list.get(pathid=Thing.hash(path[:-1])) except Thing.DoesNotExist: self.logger.log( 'failed', 'module', 'The path {0} does not exist'.format(path[:-1]) ) return # check for permission if not parent_thing.can_be_edited_by(self.user.username): self.logger.log( 'denied', 'module', 'You do not have permission to modify {0}'.format(path[:-1]) ) return self.process_module_dict(module)
def get(self, request, thing): if not request.user.has_perm(Thing.PERM_READ, ThingSubject(fullpath=thing)): return HttpResponseForbidden("Denied") hashid = Thing.hash(thing) try: thing = Thing.objects.get(pathid=hashid) typeofthing = thing.type if typeofthing is None: typeofthing = "default" context = {"thing": thing, "events": thing.get_events()} try: return render( request, "student/things/thing-events-%s.html" % typeofthing, context) except: return render(request, "student/things/thing-events-default.html", context) except Thing.DoesNotExist: return HttpResponseNotFound()
def get_uncategorised_module_for_thing(self, orm, parent_thing): fullpath = "{}/{}".format(parent_thing.fullpath, "uncategorised") pathid = RealThing.hash(fullpath) assert pathid assert "/" in fullpath assert fullpath.endswith("uncategorised") assert parent_thing.pk is not None thing, created = orm.Thing.objects.get_or_create(pathid=pathid, defaults={ "parent": parent_thing, "fullpath": fullpath, "name": "uncategorised", "fullname": "Uncategorised", "type": "module" }) return thing
def set_metadata(self, crsid, metadata): fake_user = FakeUser(username=crsid) user_thing = Thing.get_or_create_user_thing(fake_user) user_thing.metadata["studentinfo"] = metadata user_thing.save()
def get_subject_pathid(self): return Thing.hash(self.kwargs["thing"])
def _get_thing(self): if self._thing is None: self._thing = Thing.objects.get(pathid=Thing.hash(self.fullpath)) return self._thing
def load_calendar_data(self, nameFilter, caldir, listOnly, eventSourceLevel="file"): # Scan the eventdata subdir # For each json file found parse and load """ { "name": "Systems Biology", "vhash": "0f5a767acaf26ba26dfb8bcc40bea479", "organiser": "Example organiser", "groups": [ { "term": "Michaelmas", "code": "Mi1-8 Th 10", "name": "Lecture", "elements": [ { "what": "Systems Biology", "code": " x8", "who": "Example person", "when": " x8", "merge": 0, "eid": "Ee8143b54e72ed0ea44ea140fb3ef7eb4", "where": "Example location" } ] }, ........... ], "where": "Example location", "id": "T0001000012012002", "metadata": { "notes": "", "course-description": "" } } { "years": [ { "triposes": [ { "parts": [ { "name": "Architecture Tripos Part IA", "id": "T0014001202012" }, .... ], "name": "Architecture & History of Art" }, """ caldir = os.path.abspath(caldir) topF = open("%s/top.json" % caldir) top = json.loads(topF.read()) topF.close() detail_files = [] detailMatch = re.compile("details_T\d*.json$") files = os.listdir(caldir) for fileName in files: if detailMatch.match(fileName) is not None: detail_files.append(fileName) total_events = 0 for year in top["years"]: log.info("Processing Year %s " % year['name']) start_year = int(re.match("(\d{4})", year['name']).group(1)) for tripos in year['triposes']: if not 'parts' in tripos or \ len(tripos['parts']) == 0 or \ 'id' not in tripos['parts'][0]: log.info("Skipping Invalid Tripos %s " % tripos) continue if listOnly: log.info("Processing Tripos %s" % (tripos['name'])) triposId = self._parseId(tripos['parts'][0]['id']) if triposId is None: continue partsProcessed = 0 for p in tripos['parts']: n = 0 nameParts = self._parsePartName(p["name"]) triposId = self._parseId(p['id']) if nameParts is None: nameParts = {"name": p["name"]} log.error("Failed to parse name %s " % p["name"]) ''' if 'level' in nameParts: partId = "%s::%s" % (nameParts['name'], nameParts['level']) else: partId = "%s::" % (nameParts['name']) if nameFilter is not None and partId.lower() not in nameFilter: if listOnly: log.info("Skipping Part triposId %s Part %s " % (triposCode, partId)) continue ''' log.info("Processing Part triposId %s Part %s " % (p["name"], nameParts)) if listOnly: continue partsProcessed = partsProcessed + 1 dre = re.compile("details_%s\d*.json$" % p['id']) # log.info("Scanning with pattern %s " % dre.pattern) for dfn in detail_files: if not dre.match(dfn): continue detailFileName = "%s/%s" % (caldir, dfn) # log.info("Found %s " % detailFileName) detailF = open(detailFileName) detail = json.loads(detailF.read()) detailF.close() groupTitle = "Unknown" level = "0" if "name" in detail: groupTitle = detail['name'] elif "subject" in nameParts: groupTitle = nameParts['subject'] elif "name" in nameParts: level = nameParts['name'] groupTitle = nameParts['name'] name = nameParts['name'] level = self._get_level(nameParts) module = self._get_module(detail) u = [] for x in [ self._tripos_for_url(name), level, self._for_url(module) ]: if x is not None: u.append(x) thingpath = "tripos/%s" % ("/".join(u)) types = [] if self._tripos_for_url(name) is not None: types.append("tripos") if level is not None: types.append("level") if module is not None: types.append("module") thing = Thing.create_path(thingpath, { "fullname": detail['name'][:(MAX_NAME_LENGTH - 1)] }, types) if eventSourceLevel == "file": source = self.loadEventSource( groupTitle[:(MAX_NAME_LENGTH - 1)], dfn[:(MAX_URL_LENGTH - 1)]) EventSourceTag.objects.get_or_create( thing=thing, eventsource=source) # Todo: create an Thing here to associate with the source departments/department/subject/name if "groups" in detail: events = [] n = 0 sources = 0 for g in detail['groups']: if eventSourceLevel == "group": source = self.loadEventSource( ("%s %s" % (groupTitle, n))[:(MAX_NAME_LENGTH - 1)], ("%s:%s" % (dfn, n))[:(MAX_URL_LENGTH - 1)]) EventSourceTag.objects.get_or_create( thing=thing, eventsource=source) sources = sources + 1 group_template = g.get('code') or "" for e in g['elements']: location = e.get('where') or g.get( 'location') or "Unknown" title = e.get( 'what') or groupTitle or 'Unnamed' date_time_pattern = g.get('when') or "" if eventSourceLevel == "element": source = self.loadEventSource( ("%s %s" % (groupTitle, title))[:( MAX_NAME_LENGTH - 1)], ("%s:%s:%s" % (dfn, n, title))[:( MAX_URL_LENGTH - 1)]) EventSourceTag.objects.get_or_create( thing=thing, eventsource=source) sources = sources + 1 events.extend( generate(source, title, location, date_time_pattern, group_template, start_year)) self.bulk_create(events) total_events = total_events + len(events) log.info( "%s (%s) added %s events in %s series" % (thingpath, types[-1], len(events), sources)) log.info("Created %s events " % total_events)
def setUp(self): # make a user self.user = User( username='******', password='******', email='email' ) self.user.save() allusers = Thing( fullname='All Users', type='user', name='user' ) allusers.save() self.userthing = Thing( name='testuser', type='user', parent=allusers, fullname='A Users Calendar' ) self.userthing.save() # make tripos/api/test alltripos = Thing( fullname='All Tripos', type='tripos', name='tripos' ) alltripos.save() self.tripos = Thing( fullname='api', type='tripos', parent=alltripos, name='api' ) self.tripos.save()
class TestImport(TestCase): def setUp(self): # make a user self.user = User( username='******', password='******', email='email' ) self.user.save() allusers = Thing( fullname='All Users', type='user', name='user' ) allusers.save() self.userthing = Thing( name='testuser', type='user', parent=allusers, fullname='A Users Calendar' ) self.userthing.save() # make tripos/api/test alltripos = Thing( fullname='All Tripos', type='tripos', name='tripos' ) alltripos.save() self.tripos = Thing( fullname='api', type='tripos', parent=alltripos, name='api' ) self.tripos.save() # don't make the part yet # don't give permissions yet def tearDown(self): pass def test_xml_import_permission(self): logger = APILogger() # read the file file_full_path = os.path.join(xml_path, 'test_add.xml') with open(file_full_path) as opened_file: content = opened_file.read() # process the XML processor = processors.XMLImportProcessor(logger=logger) modules = processor.process(content) importer = importers.APIImporter(self.user, logger=logger) # try importing before the part is made for module in modules: importer.add_module_data(module) if logger.was_success(): self.fail("Imported to non-existent part") logger.clear() # make the part now self.part = Thing( fullname='test', type='part', parent=self.tripos, name='test' ) self.part.save() # try importing with no permission for module in modules: importer.add_module_data(module) if logger.was_success(): self.fail("Imported without permission") else: if logger.failed: self.fail("Failed to import data") logger.clear() # now give permissions and try again tag = ThingTag( thing=self.userthing, targetthing=self.part, annotation="admin" ) tag.save() for module in modules: importer.add_module_data(module) if not logger.was_success(): self.fail("Import failed "+logger.summary())
def get_user_pathid(self): user_thing_path = self.get_user_thing_path() if user_thing_path is None: return None return Thing.hash(self.get_user_thing_path())
def get(self, request, thing): if not request.user.has_perm( Thing.PERM_READ, ThingSubject(fullpath=thing, fulldepth=True)): return HttpResponseForbidden("Denied") try: thing = Thing.objects.get(pathid=Thing.hash(thing)) relatedthings = frozenset() relatedsources = frozenset() related_path = request.GET[ChildrenView.QUERY_RELATED] if related_path: # Get the things linked to the thing supplied by EventTag or EventSourceTag # eventtag__event__eventtag__thing__in looks for things linked to the same event # eventsourcetag__eventsource__eventsourcetag__thing for things linked to the same eventsource related_children_q = Thing.treequery([related_path]) related = Thing.objects.filter(related_children_q) contains_event_in_related = models.Q( eventtag__event__eventtag__thing__in=related, eventtag__event__current=True) contains_eventseries_in_related = models.Q( eventsourcetag__eventsource__eventsourcetag__thing__in= related, eventsourcetag__eventsource__current=True) relatedthings = frozenset( Thing.objects.filter( contains_event_in_related | contains_eventseries_in_related).values_list( "fullpath", flat=True)) # get all the sources that the target has related relatedsources = frozenset( EventSource.objects.filter( eventsourcetag__thing__in=related, current=True).values_list("id", flat=True)) raw_modules = Thing.objects.filter( parent__pathid=thing.pathid).prefetch_related( "eventsourcetag_set__eventsource", "thing_set__eventsourcetag_set__eventsource") modules = [] for raw_module in raw_modules: module = { "id": raw_module.id, "title": raw_module.fullname, "fullpath": raw_module.fullpath, "in_calendar": raw_module.fullpath in relatedthings } series = [] for eventsourcetag in raw_module.eventsourcetag_set.all(): raw_series = eventsourcetag.eventsource single_series = { "id": raw_series.id, "title": raw_series.title, "date_pattern": raw_series.metadata.get("datePattern", ""), "location": raw_series.metadata.get("location", ""), "people": raw_series.metadata.get("people", []), "in_calendar": raw_series.id in relatedsources } series.append(single_series) module["series"] = sorted_naturally(series, key=itemgetter("title")) modules.append(module) raw_links = ThingTag.objects.filter(annotation="link", thing=thing) links = [] for raw_link in raw_links: target = raw_link.targetthing if target.type == "part": name = target.parent.fullname + ", " + target.fullname else: name = target.fullname + " " + "(" + target.parent.parent.fullname + "), " + target.parent.fullname link = {"fullpath": target.fullpath, "name": name} links.append(link) context = { "modules": sorted_naturally(modules, key=itemgetter("title")), "links": sorted_naturally(links, key=itemgetter("name")) } return render(request, "student/modules-list/base.html", context) except Thing.DoesNotExist: return HttpResponseNotFound()
def post(self, request, thing): # Check if the user is logged in if request.user.is_anonymous(): return HttpResponseForbidden("Not logged in") elif not request.user.has_perm(Thing.PERM_LINK, ThingSubject(fullpath=thing)): return HttpResponseForbidden("Not your calendar") hashid = Thing.hash(thing) try: try: thing = Thing.objects.get(pathid=hashid) except Thing.DoesNotExist: path = "user/%s" % request.user.username if thing == path: thing = Thing.get_or_create_user_thing(request.user) # Delete associations first elist = self._expand(request.POST.getlist("esd")) if len(elist) > 0: EventSourceTag.objects.filter( thing=thing, eventsource__in=EventSource.objects.filter( id__in=elist)).delete() elist = self._expand(request.POST.getlist("ed")) if len(elist) > 0: EventTag.objects.filter( thing=thing, event__in=Event.objects.filter(id__in=elist)).delete() # If there is a list of things to delete, this is a little more complicated. tlist = self._expand(request.POST.getlist("td")) if len(tlist) > 0: # remove all EventTags and EventSourceTags that link this thing to Events or EventSource linked to by any child # The following query gets the decendents of all the things listed decendents = Thing.objects.filter(Thing.treequery(tlist)) # Then get the Events associated with all the decendents of all the things decendent_events = Event.objects.filter( eventtag__thing__in=decendents) # And delete all events tags on this thing, that match those events. EventTag.objects.filter(thing=thing, event__in=decendent_events).delete() # get all eventsources that are associated with the list of decendent things decendent_eventsource = EventSource.objects.filter( eventsourcetag__thing__in=decendents) EventSourceTag.objects.filter( thing=thing, eventsource__in=decendent_eventsource).delete() # Add associations elist = self._expand(request.POST.getlist("es")) if len(elist) > 0: # Delete then bulk add, note that no hooks on bulk add EventSourceTag.objects.filter( thing=thing, eventsource__in=EventSource.objects.filter( id__in=elist)).delete() items = [] for es in EventSource.objects.filter(id__in=elist, current=True): eventsourcetag = EventSourceTag(thing=thing, eventsource=es) eventsourcetag.on_pre_save() items.append(eventsourcetag) EventSourceTag.objects.bulk_create(items) elist = self._expand(request.POST.getlist("e")) if len(elist) > 0: # Delete then bulk add, note that no hooks on bulk add EventTag.objects.filter( thing=thing, event__in=Event.objects.filter(id__in=elist)).delete() items = [] for e in Event.objects.filter(id__id=elist, current=True): eventtag = EventTag(thing=thing, event=e) eventtag.on_pre_save() items.append(eventtag) EventTag.objects.bulk_create(items) tlist = self._expand(request.POST.getlist("t")) if len(tlist) > 0: # remove all EventTags and EventSourceTags that link this thing to Events or EventSource linked to by any child # The following query gets the decendents of all the things listed decendents = Thing.objects.filter(Thing.treequery(tlist)) # Then get the Events associated with all the decendents of all the things decendent_events = Event.objects.filter( eventtag__thing__in=decendents) # And delete all events tags on this thing, that match those events. EventTag.objects.filter(thing=thing, event__in=decendent_events).delete() # get all eventsources that are associated with the list of decendent things decendent_eventsource = EventSource.objects.filter( eventsourcetag__thing__in=decendents) EventSourceTag.objects.filter( thing=thing, eventsource__in=decendent_eventsource).delete() # Having deleted, we need to add, first the events bulk creating EventTags items = [] for e in decendent_events.filter(current=True): eventtag = EventTag(thing=thing, event=e) eventtag.on_pre_save() items.append(eventtag) EventTag.objects.bulk_create(items) # Next the Event Sources bulk creating EventSourceTags items = [] for es in decendent_eventsource.filter(current=True): eventtag = EventSourceTag(thing=thing, eventsource=es) eventtag.on_pre_save() items.append(eventtag) EventSourceTag.objects.bulk_create(items) return HttpResponse("ok") except Thing.DoesNotExist: return HttpResponseNotFound()