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)
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))
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