from common.models import * import xml.etree.ElementTree as ET import sys from xml.dom import minidom from .validators import check_plan_validity import re from decimal import Decimal def prettify(elem): """ :returns : A pretty-printed XML string for the Element elem """ rough_string = ET.tostring(elem, 'utf-8') reparsed = minidom.parseString(rough_string) return reparsed.toprettyxml(indent=" ") class RequestSerializer(): """ Serializes and unserializes a request, with all children (sequences, albums and plans) Format : XML Only serializes the wanted fields Entry point(s) : - serialize """ def serialize(self, req, file_name): """ Serializes the request into the given file """ request = ET.Element("request", name=req.name, scientific_program=req.scientific_program.name, target_type=req.target_type) for seq in req.sequences.all(): sequence = ET.SubElement(request, "sequence", name=seq.name, target_coords=seq.target_coords, jd1=str(seq.jd1), jd2=str(seq.jd2)) sequence.set("duration", str(seq.duration * 86400)) for alb in seq.albums.all(): album = ET.SubElement(sequence, "album", name=alb.name, detector=alb.detector.device.name) for plan in alb.plans.all(): pl = ET.SubElement(album, "plan", name=plan.name, filter=plan.filter.device.name, nb_images=str(plan.nb_images)) pl.set("duration", str(plan.duration * 86400)) with open(file_name, "w") as file: file.write(prettify(request)) def unserialize(self, xml_data, pyros_user): """ Unserializes the request read into file_name Throws exceptions in case of invalid file or values Directly saves the objects in DB The request still need to be submitted afterward ! :returns : "" (empty string) in case of success, error message (string) instead """ try: root = ET.fromstring(xml_data) except ET.ParseError as E: return "Invalid file : " + str(E) self.pyros_user = pyros_user self.request = None """ self.sequences will be a [(seq, album_list), ...], and same thing for album """ self.sequences = [] ret = self.unserialize_request(root) if ret != "": return ret self.request.save() for sequence, albums in self.sequences: sequence.request = self.request sequence.save() for album, plans in albums: album.sequence = sequence album.save() for plan in plans: plan.album = album plan.save() check_plan_validity(plan) return "" def unserialize_request(self, request): ''' Receives an xml.etree request and unserialize it ''' if request.tag != "request": return "Main object should be a request (found %s instead)" % (request.tag,) possible_attribs = ["name", "scientific_program", "target_type"] self.request = Request(pyros_user=self.pyros_user, is_alert=False, complete=False, submitted=False) for name, value in request.attrib.items(): if name not in possible_attribs: return "Unknown attribute %s in request" % (name,) if name == "scientific_program": try: sp = ScientificProgram.objects.get(name=value) self.request.scientific_program = sp except Exception as E: print(str(E)) return "Invalid scientific program %s" % (value,) else: self.request.__dict__[name] = value for sequence in request: ret = self.unserialize_sequence(sequence) if ret != "": return ret return "" def unserialize_sequence(self, sequence): if sequence.tag != "sequence": return "A request can only have 'sequence' children (found %s instead)" % (sequence.tag,) possible_attribs = ["name", "target_coords", "jd1", "jd2", "duration"] seq = Sequence(request=self.request, status=Sequence.INCOMPLETE) for name, value in sequence.attrib.items(): if name not in possible_attribs: return "Unknown attribute %s in sequence" % (name,) if name == "name": seq.name = value elif name == "target_coords": seq.target_coords = value elif name == "jd1": seq.jd1 = Decimal(value) elif name == "jd2": seq.jd2 = Decimal(value) elif name == "duration": seq.duration = Decimal(value) / 86400 # TODO: faire des checks ?? j'imagine, par exemple pour la cohérence des valeurs album_list = [] self.sequences.append((seq, album_list)) for album in sequence: ret = self.unserialize_album(album, seq, album_list) if ret != "": return ret return "" def unserialize_album(self, album, sequence, album_list): if album.tag != "album": return "A sequence can only have 'album' children (found %s instead)" % (album.tag,) possible_attribs = ["name", "detector"] alb = Album(sequence=sequence) for name, value in album.attrib.items(): if name not in possible_attribs: return "Unknown attribute %s in album" % (name,) if name == "detector": try: detector = Detector.objects.get(device__name=value) alb.detector = detector except Exception as E: print(str(E)) return "Invalid detector %s" % (value,) elif name == "name": alb.name = value plan_list = [] album_list.append((alb, plan_list)) for plan in album: ret = self.unserialize_plan(plan, alb, plan_list) if ret != "": return ret return "" def unserialize_plan(self, plan, album, plan_list): if plan.tag != "plan": return "An album can only have 'plan' children (found %s instead)" % (plan.tag,) possible_attribs = ["name", "filter", "duration", "nb_images"] pl = Plan(album=album) for name, value in plan.attrib.items(): if name not in possible_attribs: return "Unknown attribute %s in plan" % (name,) if name == "filter": try: filter = Filter.objects.get(device__name=value) pl.filter = filter except Exception as E: return "Invalid filter %s" % (value,) elif name == "name": pl.name = value elif name == "duration": pl.duration = Decimal(value) / 86400 elif name == "nb_images": pl.nb_images = int(value) # TODO: quelques checks ... plan_list.append(pl) return ""