Пример #1
0
def obs(obs_id: ObservationId = None, limit_and_offset=None):
    if obs_id is None:
        abort(300, description="ID is required")
        return

    repository = Repository()
    with repository.transaction():
        observation = repository.read_observation(obs_id)

        orbit = None

        if observation is None:
            abort(404, "Observation not found")

        files = repository.read_observation_files(observation["obs_id"],
                                                  **limit_and_offset)
        files_count = repository.count_observation_files(obs_id)
        satellite = repository.read_satellite(observation["sat_id"])

        orbit = observation
        if observation['tle'] is not None:
            # observation['tle'] is always an array of exactly 2 strings.
            orbit = parse_tle(*observation['tle'], satellite["sat_name"])

        station = repository.read_station(observation["station_id"])

    # Now tweak some observation parameters to make them more human readable
    observation = human_readable_obs(observation)

    # Now determine if there is a logged user and if there is, if this user is the owner of this
    # station. If he is, we should show the admin panel.
    user_id = 0
    owner = False
    if current_user.is_authenticated:
        user_id = current_user.get_id()

        # Check if the current user is the owner of the station.
        station_id = station['station_id']

        owner = repository.is_station_owner(user_id, station_id)

    return 'obs.html', dict(obs=observation,
                            files=files,
                            sat_name=satellite["sat_name"],
                            item_count=files_count,
                            orbit=orbit,
                            station=station,
                            is_owner=owner)
Пример #2
0
def migrate(config=None, migration_directory="db"):
    '''
    Perform migrations.

    Parameters
    ==========
    config
        Dictionary with psycopg2 "connect" method arguments.
        If None then read INI file
    migration_directory: str
        Directory with .psql files. Files must to keep naming convention:
            svarog-XX.psql
        where XX is number of database revision.

    Returns
    =======
    Function print migration status on console. Changes are save in database.

    Notes
    =====
    If any migration fail then all changes are revert.
    '''
    repository = Repository(config)

    db_version = repository.get_database_version()

    migrations = list_migrations(migration_directory)

    with repository.transaction() as transaction:
        for migration_version, migration_path in migrations:
            if migration_version <= db_version:
                print("Skip migration to %d version" % (migration_version, ))
                continue

            print("Process migration to %d version..." % (migration_version, ),
                  end="")
            with open(migration_path) as migration_file:
                content = migration_file.read()

            repository.execute_raw_query(content)
            print("OK")

        transaction.commit()

    new_db_version = repository.get_database_version()
    print("Migration complete from %d to %d!" % (db_version, new_db_version))
Пример #3
0
def receive(station_id: str, args: RequestArguments):
    '''
    Receive observation from station.

    Station must be authenticated.

    Request must have attached at least one imagery file. We accept only files
    with MIME-type and extension listed in @ALLOWED_FILE_TYPES dictionary.

    From first imagery file we create a thumbnail. Files are sorted using HTTP
    request keys.

    Request data are stored in DB. Binary files are saved in filesystem with
    unique, conflict-safe filename.

    Satellite assigned to observation must exists in database.
    '''

    files: Dict[str, WebFileLike] = request.files

    if len(files) == 0:
        abort(400, description="Missing file")

    # Filter files and create safe filenames
    uid = str(uuid.uuid4())
    items = enumerate(sorted(files.items(), key=lambda e: e[0]))
    file_entries: List[Tuple[str, WebFileLike]] = []
    for idx, (_, file_) in items:
        if not is_allowed_file(file_):
            app.logger.warning(
                f"File {file_.filename} is not allowed due to its type ({file_.mimetype}) or extension"
            )
            continue
        org_filename = secure_filename(file_.filename)
        filename = "%s-%d-%s" % (uid, idx, org_filename)
        file_entries.append((filename, file_))
        app.logger.info(
            f"Received file {file_.filename}, to be stored as {filename}")

    # Select thumbnail source file
    thumbnail_source_entry = first(
        lambda f: f[1].mimetype.startswith("image/"), file_entries)
    if thumbnail_source_entry is None:
        app.logger.info(f"No suitable images for thumbnail, using None")
        thumb_filename = None
    else:
        thumb_source_filename, _ = thumbnail_source_entry
        thumb_filename = "thumb-%s-%s" % (str(
            uuid.uuid4()), thumb_source_filename)

    # Save data in DB
    repository = Repository()
    with repository.transaction() as transaction:
        satellite = repository.read_satellite(args["sat"])
        if satellite is None:
            abort(400, description="Unknown satellite")

        sat_id = SatelliteId(satellite["sat_id"])

        tle = args.get('tle')
        if tle:
            # Remove trailing character
            tle = [line.strip() for line in tle]

        # Let's get metadata and try to run some sanity checks on it. Technically the whole thing
        # is not mandatory, so we only complain if important parameters are missing, but we
        # accept the observation anyway.
        metadata = args.get('config') or "{}"
        mandatory_tags = [
            "protocol", "frequency", "antenna", "antenna-type", "receiver",
            "lna", "filter"
        ]
        missing = []
        for t in mandatory_tags:
            if t not in metadata:
                missing.append(t)
        if len(missing):
            app.logger.warning(
                f"Received observation from station {station_id} with missing tags: {' '.join(missing)}"
            )

        observation: Observation = {
            'obs_id': ObservationId(0),
            'aos': args['aos'],
            'tca': args['tca'],
            'los': args['los'],
            'sat_id': sat_id,
            'thumbnail': thumb_filename,
            'config': metadata,
            'station_id': StationId(station_id),
            'tle': tle
        }

        app.logger.info(
            "Received observation: station_id=%s sat_id=%s config=%s rating=%s"
            % (station_id, sat_id, args.get('config'), args.get('rating')))

        obs_id = repository.insert_observation(observation)
        observation["obs_id"] = obs_id

        for filename, file_ in file_entries:
            observation_file: ObservationFile = {
                "obs_file_id": ObservationFileId(0),
                "filename": filename,
                "media_type": file_.mimetype,
                "obs_id": obs_id,
                'rating': args.get('rating')
            }
            repository.insert_observation_file(observation_file)

        transaction.commit()

    # Save files in filesystem
    root = app.config["storage"]['image_root']

    for filename, file_ in file_entries:
        path = os.path.join(root, filename)
        try:
            file_.save(path)
            app.logger.info("File %s written to %s" % (filename, root))
        except OSError as e:
            app.logger.error("Failed to write %s (image_root=%s): %s" %
                             (path, root, e))
            return abort(
                503,
                "Unable to write file %s. Disk operation error." % filename)

    if thumb_filename != None:
        # Make thumbnail (but only if suitable images were submitted, for text we're out of luck)
        thumb_source_path = os.path.join(root, thumb_source_filename)
        thumb_path = os.path.join(root, "thumbs", thumb_filename)
        app.logger.debug(f"Generating thumbnail for {thumb_source_filename}.")
        make_thumbnail(thumb_source_path, thumb_path)

    # Make charts
    station = repository.read_station(observation["station_id"])
    make_charts(observation, station, root)

    # Make sure to return the observation id to the station. This may be useful if the station wants
    # to update the observation in some way (send additional info or perhaps decide to delete it in the future).
    return 'Observation %d received.' % obs_id, 204