def test_receive_obs_error(self): """Test error handling in the receive routine.""" repository = Repository() station_id = 1 # Check what happens if the path is misconfigured (or the server is not able to write file) self.app.root = app.config["storage"][ 'image_root'] = '/nonexistent/path' secret = repository.read_station_secret(station_id) data = { 'aos': datetime.datetime(2020, 3, 28, 12, 00), 'tca': datetime.datetime(2020, 3, 28, 12, 15), 'los': datetime.datetime(2020, 3, 28, 12, 30), 'sat': 'NOAA 15', # 'notes': optional, "file0": open("tests/x.png", 'rb'), "file1": open("tests/x.png", 'rb') } header_value = get_authorization_header_value(str(station_id), secret, data) headers = {'Authorization': header_value} response = self.app.post('/receive', data=data, headers=headers) self.assertEqual(response.status_code, 503) # Check if there's appropriate entry in the log file. self.check_log([ "Failed to write /nonexistent/path/", "tests_x.png (image_root=/nonexistent/path)" ])
def _get_secret(station_id) -> bytes: ''' Fetch station secret from database ToDo: Returned value should be cached to avoid DDoS and DB call before authorization ''' repository = Repository() return repository.read_station_secret(station_id)
def login(): repository = Repository() if current_user.is_authenticated: stations = repository.owned_stations(current_user.get_id()) # list of stations l = " ".join(f"{s['name']}({s['station_id']})" for s in stations) app.logger.info("Authenticated user %s, owner of %s" % (current_user.username, l)) return render_template("login.html", user=current_user, stations=stations) form = LoginForm() if form.validate_on_submit(): app.logger.info( "Login requested for user %s, pass=%s, remember_me=%s" % (form.username.data, form.password.data, form.remember.data)) user = repository.read_user(user=form.username.data) if user is None: app.logger.info("Login failed: invalid username: %s" % form.username.data) flash("Invalid username.") return redirect(url_for("login")) u = ApplicationUser(user) if not u.check_password(form.password.data): app.logger.info("Login failed: invalid password %s for user %s" % (form.password.data, form.username.data)) flash("Invalid password.") return redirect(url_for("login")) if u.role == UserRole.BANNED: app.logger.info( "Login failed: attempt to login into disabled account %s" % form.username.data) flash("Account disabled.") return redirect(url_for("login")) app.logger.info("Login successful for user %s" % form.username.data) login_user(u, remember=form.remember.data) next_page = request.args.get("next") if not next_page or url_parse(next_page).netloc != "": next_page = url_for("index") return redirect(next_page) return render_template("login.html", form=form)
def test_db_version(self, mock_connect): repostory = Repository({}) mock_cursor = mock_connect.return_value.cursor.return_value mock_cursor.fetchone.side_effect = [{ "count": 1 }, (3, ), { "version": 15 }] version = repostory.get_database_version() self.assertEqual(version, 15)
def station(station_id=None): repository = Repository() station = repository.read_station(station_id) if station is None: abort(404, "Station not found") statistics = repository.read_station_statistics(station["station_id"]) photos = repository.read_station_photos(station_id) owners = repository.station_owners(station_id) # Now get 3 latest observations from this station filters = {"station_id": station['station_id']} latest_obs = repository.read_observations(filters=filters, limit=3, offset=0) # Get the 3 observations with the best rating best_obs = repository.read_observations(filters=filters, limit=3, offset=0, order="r.rating DESC", expr="r.rating is not NULL") x = {} x['station_id'] = station['station_id'] x['name'] = station['name'] x['coords'] = utils.coords(station['lon'], station['lat']) x['descr'] = station['descr'] x['config'] = station['config'] x['registered'] = station['registered'] x['lastobs'] = statistics["last_los"] x['firstobs'] = statistics["first_aos"] x['cnt'] = statistics["observation_count"] files = [] for photo in photos: y = {} y['filename'] = photo['filename'] y['descr'] = photo['descr'] y['sort'] = photo['sort'] files.append(y) return render_template('station.html', station=x, files=files, owners=owners, latest_obs=latest_obs, best_obs=best_obs)
def test_receive_obs(self): repository = Repository() station_id = 1 secret = repository.read_station_secret(station_id) data = { 'aos': datetime.datetime(2020, 3, 28, 12, 00), 'tca': datetime.datetime(2020, 3, 28, 12, 15), 'los': datetime.datetime(2020, 3, 28, 12, 30), 'sat': 'NOAA 15', 'config': '{"text":"note text"}', "file0": open("tests/x.png", 'rb'), "file1": open("tests/x.png", 'rb'), "tle": [ # Include trailling character "1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927 ", "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537" ], "rating": 0.75 } header_value = get_authorization_header_value(str(station_id), secret, data) headers = {'Authorization': header_value} response = self.app.post('/receive', data=data, headers=headers) self.assertEqual(response.status_code, 204) file_count = lambda dir_: len([ f for f in os.listdir(dir_) if os.path.isfile(os.path.join(dir_, f)) ]) self.assertEqual(file_count(IMAGE_ROOT), 2) self.assertEqual(file_count(os.path.join(IMAGE_ROOT, "thumbs")), 1) chart_dir = os.path.join(IMAGE_ROOT, "charts") self.assertEqual(file_count(chart_dir), 2) chart_files = sorted(os.listdir(chart_dir)) self.assertEqual(chart_files, ["by_time-1.png", "polar-1.png"]) # Todo: Need to check if the DB entries have been added. # Check if there are appropriate entries in the log file. self.check_log([ "0-tests_x.png written to tests/images", "1-tests_x.png written to tests/images" ])
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 obs_delete(obs_id: ObservationId = None): # First check if such an observation even exists. repository = Repository() observation = repository.read_observation(obs_id) if observation is None: return render_template('obs_delete.html', status=["There is no observation %s" % obs_id], obs_id=obs_id) # Second, check if the guy is logged in. if not current_user.is_authenticated: return render_template( 'obs_delete.html', status=["You are not logged in, you can't delete anything."], obs_id=obs_id) # Ok, at least this guy is logged in. Let's check who he is. user_id = current_user.get_id() # Check if the current user is the owner of the station. station = repository.read_station(observation["station_id"]) station_id = station['station_id'] owner = repository.is_station_owner(user_id, station_id) if not owner: return render_template( 'obs_delete.html', status=[ "You are not the owner of station %s, you can't delete observation %s." % (station.name, obs_id) ], obs_id=obs_id) # If you got that far, this means the guy is logged in, he's the owner and is deleting his own observation. status = obs_delete_db_and_disk(repository, obs_id) return render_template('obs_delete.html', status=status, obs_id=obs_id)
def obslist(limit_and_offset, **filters): '''This function retrieves observations list from a local database and displays it.''' aos_before_org = filters.get("aos_before") if aos_before_org is not None: # Repository uses datetime.datetime structure to bound dates and it is # exact date. # User provides datetime.date (which not include hour) and it means that # list should contain observations from @los_after day 00:00:00 hour to # @aos_before day 23:59:59.999 hour. For handle it we add 1 day to # @aos_before day before send to repository. filters["aos_before"] = aos_before_org + timedelta(days=1) repository = Repository() obslist = repository.read_observations(filters, **limit_and_offset) satellites_list = repository.read_satellites() observation_count = repository.count_observations(filters) stations_list = repository.read_stations() satellites_dict = { sat["sat_id"]: sat["sat_name"] for sat in satellites_list } stations_dict = {s["station_id"]: s["name"] for s in stations_list} for obs in obslist: obs["sat_name"] = satellites_dict[obs["sat_id"]] obs["station_name"] = stations_dict[obs["station_id"]] if aos_before_org is not None: # We send back to user the same date as user provide. filters["aos_before"] = aos_before_org # When database will contain many satellites and stations then we need # refactor this code to lazy, async read satellites and stations. return 'obslist.html', dict(obslist=obslist, item_count=observation_count, satellites=satellites_list, stations=stations_list, filters=filters)
def stations(limit_and_offset): '''This function retrieves list of all registered ground stations.''' repository = Repository() stations = repository.read_stations(**limit_and_offset) statistics = repository.read_stations_statistics(**limit_and_offset) station_count = repository.count_stations() # Now convert the data to a list of objects that we can pass to the template. stationlist = [] for station, stat in zip(stations, statistics): x = {} x['station_id'] = station['station_id'] x['name'] = station['name'] x['coords'] = utils.coords(station['lon'], station['lat']) x['descr'] = station['descr'] x['config'] = station['config'] x['registered'] = station['registered'] x['lastobs'] = stat["last_los"] x['cnt'] = stat["observation_count"] stationlist.append(x) return 'stations.html', dict(stations=stationlist, item_count=station_count)
def load_user(user_id): rep = Repository() u = rep.read_user(user=user_id) if u: return ApplicationUser(u) return None
from flask import render_template, flash, redirect from app import app from app.forms import AddTaskForm from app.usecases import AddTaskCase from app.repository import Repository repo = Repository() @app.route("/", methods=['GET', 'POST']) def index(): add_task = AddTaskCase() add_task.repo = repo form = AddTaskForm() all_tasks = repo.get_all_tasks() if form.validate_on_submit(): result = add_task.create( header=form.header.data, body=form.body.data, done=form.done.data, ) flash(f"Task created : {result}") return redirect("/") return render_template("home.html", form=form, tasks=all_tasks)
import string from app.pieces import Color, Empty, Pawn, Rook, Knight, Bishop, Queen, King from app.position import Position from app.repository import Repository POSITIONS_DELIMITER = ':' # Символ для розмежування позицій в рамках ходу збереженої гри MOVEMENTS_DELIMITER = '#' # Символ для розмежування ходів в рамках збереженої гри repository = Repository() # Об'єкт типу Repository для виклику методів класу Repository для роботи з базою даних class Chessboard(object): # Клас Chessboard який наслідує object def __init__(self): # Метод ініціалізації початкових значень для об'єкта класа Chessboard self.movements_history = [] # Історія ходів для подальшого збереження гри self.current_player_color = Color.WHITE # Колір поточного гравця self.captured_white_pieces = [] # Фігури, побиті чорними self.captured_black_pieces = [] # Фігури, побиті білими self.board = [ # Встановлення фігур на початкові позиції [Rook(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Rook(Color.BLACK)], [Knight(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Knight(Color.BLACK)], [Bishop(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Bishop(Color.BLACK)], [Queen(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Queen(Color.BLACK)], [King(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), King(Color.BLACK)], [Bishop(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Bishop(Color.BLACK)], [Knight(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Knight(Color.BLACK)], [Rook(Color.WHITE), Pawn(Color.WHITE), Empty(), Empty(), Empty(), Empty(), Pawn(Color.BLACK), Rook(Color.BLACK)] ] # Метод для перевірки, чи може поточний гравець походити даною фігурою
def __init__(self, repo_client=Repository(adapter=adapter)): self.client = repo_client
# This script generates fly-over charts for observations that have TLE # information recorded. from typing import Dict from app.controllers.receive import make_charts from app.repository import Repository, Station, StationId if __name__ == '__main__': stations: Dict[StationId, Station] = { } repository = Repository() observation_count = repository.count_observations() print("There are a total of %d observations" % observation_count) STEP = 100 limit = STEP offset = 0 index = 0 while offset < observation_count: # ToDo: Add filtration when MR with filtration will be merged. observations = repository.read_observations(limit=limit, offset=offset) print("Processing batch of %d observations" % len(observations)) for observation in observations: if observation['tle'] is None: print("No TLE info for observation %d, skipping." % observation['obs_id']) continue station_id = observation["station_id"]
def __init__(self,repo_client=Repository(adapter=MongoRepository)): self.repo_client = repo_client
def initialize(self): self.repository = Repository()
def wrapper(self, *args, **kwargs): config = self.db_config repository = Repository(config) return f(self, repository, *args, **kwargs)
def __init__(self): self.repository = Repository()
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