def get_gps_events(db, device_ids, page_number=1, per_page=30, time_from=None, time_to=None): """Retrieve gps events from the database for one or more devices Returns gps events as a list of dicts """ if isinstance(device_ids, str): device_ids = [device_ids] if time_from is None: time_from = utils.now_at_utc() - datetime.timedelta(days=90) if time_to is None: time_to = utils.now_at_utc() + datetime.timedelta(hours=1) events = [] total = 0 for ( plaform, osversion, appversion, model, time_from, time_to, latitude, longitude, accuracy, speed, speed_accuracy, altitude, altitude_accuracy, total, ) in db.execute( "{CALL getdatabyUUIDList (?,?,?,?,?)}", (",".join(device_ids), time_from, time_to, page_number, per_page), ).fetchall(): # from schema, *all* fields are nullable # so make sure that any casting handles None event = { "time_from": utils.isoformat(time_from), "time_to": utils.isoformat(time_to), "latitude": cast_nullable_float(latitude), "longitude": cast_nullable_float(longitude), "accuracy": accuracy, "speed": speed, "speed_accuracy": speed_accuracy, "altitude": altitude, "altitude_accuracy": altitude_accuracy, } events.append(event) return events, total
async def generate_and_store_pin(phone_number): pin_code = generate_pin() timestamp = utils.now_at_utc() await store_pin_code( phone_number=phone_number, pin_code=pin_code, timestamp=timestamp, ) return pin_code
def main(port): tornado.options.parse_command_line() # first-thing, check the database connection test_number = "+0010101011" for i in range(2): # perform an initial connect and query to validate our connection and permissions app_log.info("Checking access log routines") asyncio.run( sql.log_access( phone_numbers=[test_number], timestamp=utils.now_at_utc(), person_name="Smittestopp Backend", person_id="", person_organization="Simula Research Laboratory", organization="Simula Research Laboratory", legal_means="Vedlikehold", )) asyncio.run(sql.get_access_log(test_number, per_page=2)) # app_log.info("Checking gps log routine") # asyncio.run(sql.get_gps_events([device_id]), per_page=1) # after calling run, there's no loop! asyncio.set_event_loop(asyncio.new_event_loop()) app_log.info("Database connection okay!") graph.keep_jwt_keys_updated() start_app( endpoints(), port, )
async def insert_test_applog(): await sql.log_access( timestamp=utils.now_at_utc(), phone_numbers=["+0012341234"], person_name="For Etternavn", person_organization="Some Organization", person_id="", organization="Norsk Helsenett", legal_means="Some legal means", ) await sql.log_access( timestamp=utils.now_at_utc(), phone_numbers=["+0012341234"], person_name="", person_organization="Some Other Organization", person_id="", organization="Norsk Helsenett", legal_means="Some legal means", )
async def insert_pin_test_data(): with testsql.set_db_user(testsql.DB_USER_SERVICE_API): # Pin created more than a week ago - not reusable. await pin.store_pin_code( phone_number="+0012341234", pin_code="testdbpinX", timestamp=utils.now_at_utc() - timedelta(days=17), ) # Pin created less than a week ago - reusable. await pin.store_pin_code( phone_number="+0012341234", pin_code="testdbpin1", timestamp=utils.now_at_utc() - timedelta(days=6, minutes=1), ) # Pin created more than a week ago - not reusable. await pin.store_pin_code( phone_number="+0012341236", pin_code="testdbpin2", timestamp=utils.now_at_utc() - timedelta(days=7, minutes=1), )
async def fetch_or_generate_pin(phone_number, not_older_than=7): """ Fetch pin from database or generate now if too old If there are no pin codes associated with a phone number we create a new. If there are, but the latest is older than 'not_older_than', we also create a new. Otherwise we use the latest. """ threshold = utils.now_at_utc() - timedelta(days=not_older_than) pin = await get_latest_pin_code_after_threshold( phone_number=phone_number, threshold=threshold ) if pin is None: pin = await generate_and_store_pin(phone_number=phone_number) return pin
async def audit_log( self, phone_numbers, person_name=None, person_id=None, person_organization=None, organization=None, legal_means=None, ): """Store an entry in the audit log""" timestamp = utils.now_at_utc() if person_name is None: person_name = self.audit_fields["person_name"] if person_id is None: person_id = self.audit_fields["person_id"] if person_organization is None: person_organization = self.audit_fields["person_organization"] if organization is None: organization = self.audit_fields["organization"] if legal_means is None: legal_means = self.audit_fields["legal_means"] if not organization: raise ValueError("organization required!") if not legal_means: raise ValueError("legal_means required!") try: await sql.log_access( timestamp=timestamp, phone_numbers=phone_numbers, person_name=person_name, person_id=person_id, person_organization=person_organization, organization=organization, legal_means=legal_means, ) except pyodbc.InterfaceError as e: app_log.exception("Connection error in sql.log_access") raise web.HTTPError( 503, "Internal temporary server error - please try again")
async def post(self): body = self.get_json_body() phone_number = body["phone_number"] user = await self.lookup_user(phone_number) await self.audit_log(phone_numbers=[phone_number]) phone_number = user["displayName"] mask_number = user["logName"] device_ids = [] request_id = str(uuid.uuid4()) app_log.info( f"Submitting analysis jobs for {mask_number}: {request_id}") request_info = f"lookup:{request_id}:info" # create job queue in redis db = get_redis() result_keys = [] async for device_id in graph.device_ids_for_user(user): device_ids.append(device_id) result_key = f"lookup:{request_id}:result:{device_id}" result_keys.append(result_key) app_log.info(f"Submitting analysis job for {device_id}") job = { "request_id": request_id, "device_id": device_id, "result_key": result_key, "time_from": utils.isoformat(body.get("time_from")), "time_to": utils.isoformat(body.get("time_to")), "expiry": LOOKUP_RESULT_EXPIRY, } db.rpush(REDIS_JOBQUEUE_NAME, json.dumps(job).encode("utf8")) # push device id onto job queue if not device_ids: app_log.info(f"Phone number {mask_number} has no devices") self.set_status(404) self.write( json.dumps({ "phone_number": phone_number, "found_in_system": False })) return app_log.info(f"Storing request info for {request_id}") db.set( request_info, json.dumps({ "phone_number": phone_number, "result_keys": result_keys, "device_ids": device_ids, }), ex=LOOKUP_RESULT_EXPIRY, ) self.set_status(202) self.write( json.dumps({ "request_id": request_id, # todo: figure out how to get this right? # /fhi/ prefix isn't available from APIM # but it's wrong when not behind APIM "result_url": f"https://{self.request.host}/fhi/lookup/{request_id}", "result_expires": utils.isoformat(utils.now_at_utc() + datetime.timedelta( seconds=LOOKUP_RESULT_EXPIRY)), }))