def save(self, *args, **kwargs): old_slug = None if self.pk is not None: old_self = Atom.objects.get(pk=self.pk) old_slug = old_self.slug super(Atom, self).save(*args, **kwargs) # reify any orphan relationship for orphan_rel in AtomOrphanRelationship.objects.filter(ref=self.slug): # in case it already exists (is it even possible?) try: existing_rel = AtomRelationship.objects.get(from_atom=orphan_rel.atom, to_atom=self, typ=orphan_rel.typ) except ObjectDoesNotExist: reify_rel = AtomRelationship.objects.create(from_atom=orphan_rel.atom, to_atom=self, typ=orphan_rel.typ) reify_rel.save() orphan_rel.delete() from knowledge.format import extract_references, replace_references # fix references when slug is changed if self.slug != old_slug: if old_slug is None or old_slug == '': old_ref = str(self.pk) else: old_ref = old_slug if self.slug is None or self.slug == '': new_ref = str(self.pk) else: new_ref = self.slug rels = AtomRelationship.objects.filter(to_atom=self) for rel in rels: atom = rel.from_atom atom.text = replace_references(atom.text, old_ref, new_ref) atom.save() # extract references and test for relationships uptodate_rel = [] for verb, ref in extract_references(self.text): try: reltype = AtomRelationshipType.objects.get(slug=verb) except ObjectDoesNotExist: continue try: atom = Atom.objects.by_ref(ref) try: existing_rel = AtomRelationship.objects.get(from_atom=self, to_atom=atom, typ=reltype) uptodate_rel.append(existing_rel) except ObjectDoesNotExist: arel = AtomRelationship.objects.create(from_atom=self, to_atom=atom, typ=reltype) arel.save() uptodate_rel.append(arel) except ObjectDoesNotExist: arel = AtomOrphanRelationship.objects.create(atom=self, ref=ref, typ=reltype) arel.save() extra_rel = AtomRelationship.objects.filter(from_atom=self) \ .exclude(id__in=[ rel.id for rel in uptodate_rel ]) for extra in extra_rel: extra.delete()
def clean_source(self): source = self.cleaned_data['source'] # Atom bulk format # (% is choosen as lead symbol as it's tex comment symbol) # %Atom <atom type slug> # %Ref [internal ref] # %Slug <slug> # %Name <name> # ... text ... lines = source.replace('\r','').split('\n') i = 0 current_atom = None atoms = [] for line in lines: if line == '': if current_atom is not None and current_atom['text'] != '': current_atom['text'] += '\r\n' continue if line[0] == '%': tokens = line[1:].split() command = tokens[0] arg = line[1+len(command):].strip() if command.lower() == 'atom': if current_atom is not None: if 'ref' not in current_atom: current_atom['ref'] = str(len(atoms)+1) atoms.append(current_atom) current_atom = { 'type' : arg, 'text' : '' } if command.lower() == 'ref': current_atom['ref'] = arg if command.lower() == 'slug': current_atom['slug'] = arg if command.lower() == 'name': current_atom['name'] = arg elif current_atom is not None: current_atom['text'] += line + '\r\n' if current_atom is not None: if 'ref' not in current_atom: current_atom['ref'] = str(len(atoms)+1) atoms.append(current_atom) internal_refs = [] for atom in atoms: internal_refs.append( atom['ref'] ) slug_re = re.compile(r'^[\w_]+$') from knowledge.format import extract_references edges_incoming = {} edges_outgoing = {} atom_by_ref = {} for atom in atoms: atom['refs'] = extract_references(atom['text']) aref = atom['ref'] atom_by_ref[aref] = atom edges_outgoing[aref] = [] if aref not in edges_incoming: edges_incoming[aref] = [] for _, ref in atom['refs']: if ref in internal_refs: if ref not in edges_incoming: edges_incoming[ref] = [] edges_incoming[ref].append(aref) edges_outgoing[aref].append(ref) s = [] for atom in atoms: if edges_outgoing[atom['ref']] == []: s.append(atom) atom['outgoing'] = [] ordered = [] while len(s) > 0: a = s[0] ordered.append(a) s = s[1:] for oaref in edges_incoming[a['ref']]: edges_outgoing[oaref].remove(a['ref']) atom_by_ref[oaref]['outgoing'].append( a ) if edges_outgoing[oaref] == []: s.append(atom_by_ref[oaref]) for atom in atoms: if edges_outgoing[atom['ref']] != []: raise forms.ValidationError("Cycle impliquant $%s" % atom['ref']) for atom in atoms: if 'slug' in atom and slug_re.match(atom['slug']) is None: raise forms.ValidationError("Slug invalide %s" % atom['slug']) typ = atom['type'] if AtomType.objects.filter(slug=typ).count() == 0: raise forms.ValidationError("Type inconnu %s" % typ) self.ordered = ordered return source