コード例 #1
0
    def process_GET(self, request):
        """
        Function that will be called upon receiving a GET request for the
        aforementioned URL.
        """
        # Parse the given parameters.
        event_id = request.args0.get("event", None)
        channel_id = request.args0.get("channel_id", None)
        station_id = request.args0.get("station_id", None)
        tag = request.args0.get("tag", "")
        format = request.args0.get("format", None)

        # An event id is obviously needed.
        if event_id is None:
            msg = ("No event parameter passed. Every waveform "
                "is bound to an existing event.")
            raise InvalidParameterError(msg)

        if event_id is not None and not event_exists(event_id, self.env):
            msg = "The given event resource name '%s' " % event_id
            msg += "is not known to SeisHub."
            raise InvalidParameterError(msg)

        # Returns different things based on parameter combinations.
        # Return all waveforms available for a given event.
        if channel_id is None and station_id is None and event_id is not None:
            return self.getListForEvent(event_id, request)
        # Return all waveforms available for a given event and station id.
        elif station_id is not None and event_id is not None:
            return self.getListForStationAndEvent(event_id, station_id,
                request)

        # At this step format will mean a waveform output format.
        acceptable_formats = ["mseed", "sac", "gse2", "segy", "raw", "json"]
        if format and format.lower() not in acceptable_formats:
            msg = "'%s' is an unsupported format. Supported formats: %s" % \
                (format, ", ".join(acceptable_formats))
            raise InvalidParameterError(msg)

        if channel_id is None:
            msg = ("To download a waveform, 'channel_id' to be specified.")
            raise InvalidParameterError(msg)

        split_channel = channel_id.split(".")
        if len(split_channel) != 4:
            msg = "Invalid 'channel_id'. Needs to be NET.STA.LOC.CHAN."
            raise InvalidParameterError(msg)

        network, station, location, channel = split_channel

        session = self.env.db.session(bind=self.env.db.engine)
        station_id = get_station_id(network, station, session)
        if station_id is False:
            session.close()
            msg = "Could not find station %s.%s in the database" % \
                (network, station)
            raise InvalidParameterError(msg)

        query = session.query(WaveformChannelObject)\
            .join(ChannelObject)\
            .filter(WaveformChannelObject.event_resource_id == event_id)\
            .filter(WaveformChannelObject.tag == tag)\
            .filter(ChannelObject.location == location)\
            .filter(ChannelObject.channel == channel)\
            .filter(ChannelObject.station_id == station_id)

        try:
            result = query.one()
        except sqlalchemy.orm.exc.NoResultFound:
            session.close()
            msg = "No matching data found in the database."
            raise NotFoundError(msg)

        if format and format.lower() == "raw":
            with open(result.filepath.filepath, "rb") as open_file:
                data = open_file.read()
            # Set the corresponding headers.
            request.setHeader("content-type", "application/octet-stream")
            filename = os.path.basename(result.filepath.filepath)\
                .encode("utf-8")
            request.setHeader("content-disposition", "attachment; filename=%s"
                % filename)
            return data

        chan = result.channel
        stat = chan.station
        network = stat.network
        station = stat.station
        location = chan.location
        channel = chan.channel
        starttime = UTCDateTime(result.starttime) if result.starttime else None
        endtime = UTCDateTime(result.endtime) if result.endtime else None
        default_format = result.format

        # Read and filter the file.
        st = read(result.filepath.filepath).select(network=network,
            station=station, location=location, channel=channel)
        session.close()

        # Now attempt to find the correct trace in case of more then one trace.
        # This should enable multicomponent files.
        selected_trace = None
        for tr in st:
            if (starttime and abs(tr.stats.starttime - starttime) > 1) or \
                    (endtime and abs(tr.stats.endtime - endtime) > 1):
                continue
            selected_trace = tr
            break

        if selected_trace is None:
            msg = "Could not find the corresponding waveform file."
            raise InternalServerError(msg)

        # Deal with json format conversion.
        if format and format == "json":
            output = {
                "channel": selected_trace.id,
                "sampling_rate": selected_trace.stats.sampling_rate,
                "npts": selected_trace.stats.npts,
                "data": []
            }
            time = selected_trace.stats.starttime
            delta = selected_trace.stats.delta
            for value in selected_trace.data:
                output["data"].append([time.isoformat(), float(value)])
                time += delta
            request.setHeader('content-type',
                'application/json; charset=UTF-8')
            return json.dumps(output)

        # XXX: Fix some ObsPy modules to be able to write to memory files.
        tempfile = NamedTemporaryFile()
        if format:
            default_format = format
        selected_trace.write(tempfile.name, format=default_format)
        with open(tempfile.name, "rb") as open_file:
            data = open_file.read()
        tempfile.close()
        os.remove(tempfile.name)

        # Set the corresponding headers.
        request.setHeader("content-type", "application/octet-stream")
        filename = "%s.%s" % (selected_trace.id, default_format.lower())
        filename = filename.encode("utf-8")
        request.setHeader("content-disposition", "attachment; filename=%s" %
            filename)
        return data
コード例 #2
0
    def process_POST(self, request):
        """
        Function that will be called upon receiving a POST request for the
        aforementioned URL.
        """
        # Parse the given parameters.
        event_id = request.args0.get("event", None)
        is_synthetic = request.args0.get("synthetic", None)
        tag = request.args0.get("tag", "")
        if isinstance(is_synthetic, basestring) and \
                is_synthetic.lower() in lowercase_true_strings:
            is_synthetic = True
        else:
            is_synthetic = False

        # Every waveform MUST be bound to an event.
        if event_id is None:
            msg = ("No event parameter passed. Every waveform "
                "must be bound to an existing event.")
            raise InvalidParameterError(msg)

        if not event_exists(event_id, self.env):
            msg = "The given event resource name '%s' " % event_id
            msg += "is not known to SeisHub."
            raise InvalidParameterError(msg)

        # There are two possibilities for getting data inside the database:
        # upload the file directly to SeisHub or just give a file URL that the
        # server can find.
        filename = request.args0.get("index_file", None)
        # If the 'index_file' parameter is not given, assume the file will be
        # directly uploaded.
        if filename is None:
            waveform_data = request.content
            waveform_data.seek(0, 0)
            file_is_managed_by_seishub = True
        else:
            filename = os.path.abspath(filename)
            if not os.path.exists(filename) or \
                    not os.path.isfile(filename):
                msg = "File '%s' cannot be found by the SeisHub server." % \
                    filename
                raise InvalidParameterError(msg)
            with open(filename, "rb") as open_file:
                waveform_data = StringIO(open_file.read())
            waveform_data.seek(0, 0)
            file_is_managed_by_seishub = False

        # Check if file exists. Checksum is returned otherwise. Raises on
        # failure.
        data = waveform_data.read()
        waveform_data.seek(0, 0)
        md5_hash = check_if_file_exist_in_db(data, self.env)

        msg = ("The data does not appear to be a valid waveform file. Only "
               "data readable by ObsPy is acceptable.")
        # Only valid waveforms files will be stored in the database. Valid is
        # defined by being readable by ObsPy.
        try:
            st = read(waveform_data)
        except:
            raise InvalidObjectError(msg)

        # Replace network, station and channel codes with placeholders if they
        # do not exist. Location can be an empty string.
        network = st[0].stats.network if st[0].stats.network else "XX"
        station = st[0].stats.station if st[0].stats.station else "XX"
        location = st[0].stats.location
        channel = st[0].stats.channel if st[0].stats.channel else "XX"

        # Check if the tag is valid, e.g. that it fulfulls the constraints of
        # being unique per channel_id and event.
        tags = get_all_tags(network, station, location, channel, event_id,
            self.env)
        if tag in tags:
            msg = "Tag already exists for the given channel id and event."
            raise InvalidParameterError(msg)

        if file_is_managed_by_seishub is True:
            # Otherwise create the filename for the file, and check if it
            # exists.
            filename = os.path.join(self.env.config.get("event_based_data",
                "waveform_filepath"), event_id,
                ("{network}.{station}.{location}.{channel}-"
                "{year}_{month}_{day}_{hour}"))
            t = st[0].stats.starttime
            filename = filename.format(network=network, station=station,
                channel=channel, location=location, year=t.year, month=t.month,
                day=t.day, hour=t.hour)

            # Write the data to the filesystem. The final filename is returned.
            filename = write_string_to_filesystem(filename, data)

        # Use only one session to be able to take advantage of transactions.
        session = self.env.db.session(bind=self.env.db.engine)

        # Wrap in try/except and rollback changes in case something fails.
        try:
            # Add information about the uploaded file into the database.
            filepath = add_filepath_to_database(session, filename, len(data),
                    md5_hash, is_managed_by_seishub=file_is_managed_by_seishub)

            # Loop over all traces in the file.
            for trace in st:
                stats = trace.stats

                # Extract coordinates if it is a sac file. Else set them to
                # None.
                if hasattr(stats, "sac"):
                    # Invalid floating point value according to the sac
                    # definition.
                    iv = -12345.0
                    sac = stats.sac
                    latitude = sac.stla if sac.stla != iv else None
                    longitude = sac.stlo if sac.stlo != iv else None
                    elevation = sac.stel if sac.stel != iv else None
                    local_depth = sac.stdp if sac.stdp != iv else None
                else:
                    latitude, longitude, elevation, local_depth = [None] * 4

                # Add the channel if it does not already exists, or update the
                # location or just return the existing station. In any case a
                # channel column object will be returned.
                channel_row = add_or_update_channel(session,
                    stats.network, stats.station, stats.location,
                    stats.channel, latitude, longitude, elevation, local_depth)

                # Add the current waveform channel as well.
                waveform_channel = WaveformChannelObject(
                    channel=channel_row, filepath=filepath,
                    event_resource_id=event_id,
                    starttime=stats.starttime.datetime,
                    endtime=stats.endtime.datetime, tag=tag,
                    sampling_rate=stats.sampling_rate, format=stats._format,
                    is_synthetic=is_synthetic)
                session.add(waveform_channel)

                session.commit()
        except Exception, e:
            # Rollback session.
            session.rollback()
            session.close()

            # Remove the file if something failes..
            if file_is_managed_by_seishub:
                os.remove(filename)
            msg = e.message + " - Rolling back all changes."
            raise InternalServerError(msg)