def set_profile_picture(user_uuid, picture_url): query = datastore.get_client().query(kind='Users') query.add_filter('user_uuid', '=', user_uuid) user = list(query.fetch(1))[0] user['profile_image'] = picture_url datastore.get_client().put(user)
def submit_horticulture_measurements(): """Save horticulture measurements from a device. .. :quickref: Horticulture; Save horticulture measurements :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string device_uuid: Device UUID in which plant was measured :<json string leaves_count: Leaf count :<json string plant_height: Plant height in cm **Example response**: .. sourcecode:: json { "message": "Measurements saved.", "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get('user_token') device_uuid = received_form_response.get('device_uuid') user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None or device_uuid is None: return error_response(message='Invalid User: Unauthorized.') leaves_count = received_form_response.get("leaves_count") plant_height = received_form_response.get("plant_height") horticulture_notes = received_form_response.get("horticulture_notes") if leaves_count == None and plant_height == None and horticulture_notes == None: return error_response() # Add the user to the users kind of entity key = datastore.get_client().key('HorticultureMeasurements') # Indexes every other column except the description horitculture_reg_task = gcds.Entity(key, exclude_from_indexes=[]) horitculture_reg_task.update({ 'device_uuid': device_uuid, 'measurement': json.dumps({ "leaves_count": leaves_count, "plant_height": plant_height, "horticulture_notes": horticulture_notes, }), "modified_at": datetime.now() }) datastore.get_client().put(horitculture_reg_task) return success_response(message="Measurements saved.")
def delete_recipe(): """Delete a users recipe. .. :quickref: Recipe; Delete recipe :reqheader Accept: application/json :<json string user_token: User's Token. :<json string recipe_uuid: Recipe UUID. **Example response**: .. sourcecode:: json { "message": "success", "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get("user_token", "") recipe_uuid = received_form_response.get("recipe_uuid") testing = received_form_response.get("testing") if user_token is None or recipe_uuid is None: return error_response( message="Please make sure you have added values for all the fields" ) # Get user uuid associated with this sesssion token user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response(message="Invalid User: Unauthorized") # If pytest is calling this, don't actually save a recipe if testing: return success_response(message="test worked") # Get the recipe recipe = datastore.get_one_from_DS('Recipes', 'recipe_uuid', recipe_uuid) if recipe is None: return error_response(message="Invalid recipe uuid.") # Only delete the recipe if it has the users user_uuid if recipe.get('user_uuid') != user_uuid: return error_response(message="User does not own this recipe.") # Delete it. datastore.get_client().delete(key=recipe.key) return success_response(message="Successfully saved.")
def verify_user_session(): """Verify the user's session token is still valid. .. :quickref: Authentication; Verify user's session :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. **Example response**: .. sourcecode:: json { "message": "Successful", "is_expired": "True", "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get("user_token", None) query_session = datastore.get_client().query(kind="UserSession") query_session.add_filter('session_token', '=', user_token) query_session_result = list(query_session.fetch()) expired = True user_uuid = None if len(query_session_result) > 0: user_uuid = query_session_result[0].get("user_uuid", None) session_expiration = query_session_result[0].get("expiration_date") if session_expiration is not None: expired = is_expired(session_expiration) return success_response( message="Successful", is_expired=expired, user_uuid=user_uuid )
def signup_user_oauth(): username = g.user_info["sub"] email_address = g.user_info["email"] password = ''.join([ random.choice(string.ascii_letters + string.digits) for n in range(32) ]) # received_form_response.get("password") organization = None #received_form_response.get("organization") testing = False # received_form_response.get("testing") if not (username and email_address): return error_response( message="Please make sure you have added values for all the fields" ) if not is_email(email_address, check_dns=True): return error_response(message="Invalid email.") if testing: # our pytest is hitting this API, so don't create the user return success_response() new_user = User(username=username, password=password, email_address=email_address, organization=organization) user_uuid = new_user.insert_into_db(datastore.get_client()) return new_user
def remove_command(self, device_ID: str, command: str) -> None: # get any existing entity for this command and delete it. entity = self.get_command_entity(device_ID, command) if entity is not None: # delete this entity DS = datastore.get_client() DS.delete(key=entity.key) logging.debug(f'{self.name}.remove_command {command}')
def get_device_peripherals(): """Get peripherals. Used for recipe editor. .. :quickref: Recipe; Get peripherals :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string selected_peripherals: Comma separated list of peripheral UUIDs **Example Response**: .. sourcecode:: json { "results":[{ "name": "Name", "sensor_name": "Sensor Name", "type": "Sensor Type", "color": "#FFAA00", "inputs": "inputs" }] } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get("user_token") peripherals_string = received_form_response.get("selected_peripherals") if user_token is None or peripherals_string is None: return error_response( message="Access denied." ) peripheral_details = [] peripherals_array = peripherals_string.split(",") for peripheral in peripherals_array: if len(peripheral) == 0: return error_response() query = datastore.get_client().query(kind='Peripherals') query.add_filter('uuid', '=', str(peripheral)) peripheraldetails = list(query.fetch()) if len(peripheraldetails) == 0: return error_response() peripheral_detail_json = { "name":peripheraldetails[0]["name"], "sensor_name":peripheraldetails[0]["sensor_name"], "type":peripheraldetails[0]["type"], "color":"#"+peripheraldetails[0]["color"], "inputs": peripheraldetails[0]["inputs"] } peripheral_details.append(peripheral_detail_json) return success_response( results=peripheral_details )
def login(): """Log a user into this API, returns a session token. .. :quickref: Authentication; Log in :reqheader Accept: application/json :<json string username: Users username (from the /api/signup API call) :<json string password: Users password (from the /api/signup API call) **Example response**: .. sourcecode:: json { "user_uuid": "Users UUID from the registration process", "user_token": "token string", "is_admin": False "message": "Login Successful" "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) username = received_form_response.get("username") password = received_form_response.get("password") if not (username and password): return error_response( message="Please make sure you have added values for all the fields" ) user = User(username=username, password=password) user_uuid, is_admin = user.login_user(client=datastore.get_client()) if user_uuid is None: return error_response( message="Login failed. Please check your credentials.") session_token = UserSession(user_uuid=user_uuid).insert_into_db( client=datastore.get_client()) return success_response(user_uuid=user_uuid, user_token=session_token, is_admin=is_admin, message="Login Successful")
def remove_all_commands(self, device_ID: str) -> None: # get list of all entities by time entities = datastore.get_sharded_entities( datastore.DS_device_data_KIND, self.schedule_property, device_ID) if 0 == len(entities): return None DS = datastore.get_client() for e in entities: DS.delete(key=e.key) logging.debug(f'{self.name}.remove_all_commands done.')
def save_user_profile_changes(): """Update the users' profile information. .. :quickref: User; Update profile :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string email_address: Users email address :<json string username: Users username (and login name) :<json string organization: The organizaion the user is associated with **Example response**: .. sourcecode:: json { "profile_image": "previously saved profile image", "username": "******", "email_address": "saved email address", "organization": "saved organization", "response_code": 200 } """ received_form_response = request.get_json() user_token = received_form_response.get("user_token") if user_token is None: return error_response( message="Please make sure you have added values for all the fields" ) user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response(message="Invalid User: Unauthorized") query = datastore.get_client().query(kind='Users') query.add_filter('user_uuid', '=', user_uuid) user = list(query.fetch(1))[0] # This checks if inputs are empty strings as well. email_address = get_non_empty(received_form_response, 'email_address', user['email_address']) username = get_non_empty(received_form_response, 'username', user['username']) org = get_non_empty(received_form_response, 'organization', user['organization']) if not datastore.update_user(user_uuid, username, email_address, org): return error_response("nope") return success_response(profile_image=user.get('profile_image'), username=user.get('username'), email_address=user.get('email_address'), organization=user.get('organization'))
def ack(self, device_ID: str, notification_ID: str) -> None: entities = datastore.get_sharded_entities( datastore.DS_device_data_KIND, self.dd_property, device_ID) for e in entities: data = e.get(datastore.DS_DeviceData_data_Property, {}) if data.get(self.ID_key) == notification_ID: # delete this entity (as a form of acknowledging it and # keeping the list of notifications from growing without # bounds). DS = datastore.get_client() DS.delete(key=e.key) break
def get_device_images(): """Returns all images associated with device. .. :quickref: Sensor Data; Image list :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string device_uuid: Device UUID **Example response**: .. sourcecode:: json { "image_urls": ["URL1", "URL2"], "response_code": 200 } """ parameters = request.get_json() user_token = parameters.get('user_token') user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response( message='Invalid token. Unauthorized.' ) device_uuid = parameters.get('device_uuid') if device_uuid is None: return error_response( message='No device_uuid submitted.' ) # Sort by date descending and take the first 50 # This is equivalent to taking the most recent 50 images # Then, reverse the order so it's chronological image_query = datastore.get_client().query(kind="Images", order=['-creation_date']) image_query.add_filter('device_uuid', '=', device_uuid) images = list(image_query.fetch(100))[::-1] if not images: return error_response( message='No images associated with device.' ) image_urls = list(map(decode_url, images)) return success_response( image_urls=image_urls )
def get_devices_for_user(user_uuid): query = datastore.get_client().query(kind='Devices') query.add_filter('user_uuid', '=', user_uuid) query_results = list(query.fetch()) devices = [] for device in query_results: device_json = pre_serialize_device(device) print(' {}, {}, {}'.format(device_json['device_uuid'], device_json['device_reg_no'], device_json['device_name'])) devices.append(device_json) return devices
def oauth_login(): client = datastore.get_client() query = client.query(kind='Users') query.add_filter('email_address', '=', g.user_info['email']) query_result = list(query.fetch(1)) if not query_result: user = signup_user_oauth() user_uuid = user.user_uuid is_admin = False # TODO: This shouldn't come from here but from the tokens... else: user = query_result[0] user_uuid = user.get('user_uuid') is_admin = user.get('is_admin', False) if user_uuid is None: return error_response( message="Login failed. Please check your credentials.") session_token = UserSession(user_uuid=user_uuid).insert_into_db( client=datastore.get_client()) return success_response(user_uuid=user_uuid, user_token=session_token, is_admin=is_admin, message="Login Successful")
def signup(): """Create a user account. .. :quickref: Authentication; Create account :reqheader Accept: application/json :<json string username: Users login name :<json string email_address: Users email address :<json string password: Users password :<json string organization: Users organization (self chosen) **Example response**: .. sourcecode:: json { "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) username = received_form_response.get("username") email_address = received_form_response.get("email_address") password = received_form_response.get("password") organization = received_form_response.get("organization") testing = received_form_response.get("testing") if not (username and email_address and password): return error_response( message="Please make sure you have added values for all the fields" ) if not is_email(email_address, check_dns=True): return error_response(message="Invalid email.") if testing: # our pytest is hitting this API, so don't create the user return success_response() user_uuid = User(username=username, password=password, email_address=email_address, organization=organization).insert_into_db( datastore.get_client()) if user_uuid: return success_response() else: return error_response(message="User creation failed.")
def get_user_image(): """Get user profile information. .. :quickref: User; User profile :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. **Example Response**: .. sourcecode:: json { "profile_image": null, "username": "******", "email_address": "*****@*****.**", "organization": "Example Foundation", "response_code": 200 } """ received_form_response = request.get_json() user_token = received_form_response.get("user_token") if user_token is None: return error_response( message="Please make sure you have added values for all the fields" ) user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response( message="Invalid User: Unauthorized" ) query = datastore.get_client().query(kind='Users') query.add_filter('user_uuid', '=', user_uuid) user = list(query.fetch(1))[0] return success_response( profile_image=user.get('profile_image'), username=user.get('username'), email_address=user.get('email_address'), organization=user.get('organization') )
def stop(self, device_ID: str) -> None: # have to get the datastore entity to update it. entities = datastore.get_sharded_entities( datastore.DS_device_data_KIND, self.runs_property, device_ID, count=1) if 0 == len(entities): logging.error(f'{self.name}.stop no current run for {device_ID}') return e = entities[0] # only one entity in the list # get this entities data property and update it run = e.get(datastore.DS_DeviceData_data_Property, {}) run[self.end_key] = dt.datetime.utcnow().strftime('%FT%XZ') # put entity back in datastore DS = datastore.get_client() DS.put(e) logging.debug(f'{self.name}.stopped run {run}')
def get_plant_types(): """Get known plant types. For the recipe editor. .. :quickref: Utility; Plant types **Example Response**: .. sourcecode:: json { "results": [ { "name": "Basil", "variants": "Sweet Basil, Purple Basil" }, { "name": "Lettuce", "variants": "Iceberg, Butterhead, Rocket, Mizuna, Romaine" } ], "response_code": 200 } """ received_form_response = request.get_json() user_token = received_form_response.get("user_token") if user_token is None: return error_response(message="Access denied.") query = datastore.get_client().query(kind='Plants') query_result = list(query.fetch()) results = list(query_result) results_array = [] for result in results: plant_type_json = { 'name': result['name'], 'variants': result['variants'] } results_array.append(plant_type_json) return success_response(results=results_array)
def get_device_types(): """Get a list of all device types. .. :quickref: Utility; Device types :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. **Example Response**: .. sourcecode:: json { "results": [{ "name": "EDU", "device_type_id": "Type-UUID", "peripherals": ["P1-UUID", "P2-UUID", "P3-UUID"] }], "response_code": 200 } """ received_form_response = request.get_json() user_token = received_form_response.get("user_token") if user_token is None: return error_response(message="Access denied.") query = datastore.get_client().query(kind='DeviceType') query_result = list(query.fetch()) results = list(query_result) results_array = [] for result in results: device_type_json = { 'peripherals': result['peripherals'], 'device_type_id': result['id'], 'name': result['name'] } results_array.append(device_type_json) return success_response(results=results_array)
def get_recipe_by_uuid(): """Return the recipe. Used to build an editor to modify this recipe. .. :quickref: Recipe; Recipe details :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string recipe_uuid: Recipe UUID to look up **Example response**: .. sourcecode:: json "recipe": "recipe JSON string", "devices": "user devices", "response_code": 200 } """ received_form_response = request.get_json() user_token = received_form_response.get("user_token") recipe_uuid = received_form_response.get("recipe_uuid") if user_token is None or recipe_uuid is None: return error_response( message="Please make sure you have added values for all the fields" ) user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response(message="Invalid User: Unauthorized") devices = get_devices_for_user(user_uuid) # Get queried recipe recipes_query = datastore.get_client().query(kind='Recipes') recipes_query.add_filter("recipe_uuid", "=", recipe_uuid) recipes_query_results = list(recipes_query.fetch()) if 0 < len(recipes_query_results): return success_response(recipe=recipes_query_results[0]["recipe"], devices=devices) return error_response(message="No recipe with that UUID")
def get_current_horticulture_log(device_uuid): # Initialize variables plant_height = None leaf_count = None submitted_at = None horticulture_notes = None # Query datastore query = datastore.get_client().query(kind="DailyHorticultureLog") query.add_filter("device_uuid", "=", device_uuid) query_result = list(query.fetch()) # Validate results if len(query_result) == 0: return { "leaf_count": None, "plant_height": None, "submitted_at": None, "horticulture_notes": None } # Parse results for result in query_result: if not plant_height and "plant_height" in result: plant_height = result["plant_height"] if not leaf_count and "leaf_count" in result: leaf_count = result["leaf_count"] if not submitted_at and "submitted_at" in result: submitted_at = result["submitted_at"] if not horticulture_notes and "horticulture_notes" in result: horticulture_notes = result["horticulture_notes"] if plant_height and leaf_count and submitted_at and horticulture_notes: break return { "plant_height": plant_height, "leaf_count": leaf_count, "submitted_at": submitted_at, "horticulture_notes": horticulture_notes, }
def apply_to_device(): """Run a recipe on a device. .. :quickref: Recipe; Run recipe, run :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string device_uuid: UUID of device to apply recipe to :<json string recipe_uuid: UUID of recipe to run **Example response**: .. sourcecode:: json { "response_code": 200 } """ # Get request parameters request_json = json.loads(request.data.decode("utf-8")) device_uuid = request_json.get("device_uuid", None) recipe_uuid = request_json.get("recipe_uuid", None) user_token = request_json.get("user_token", None) # Validate parameters if device_uuid is None: return Response(json.dumps({"message": "Device UUID is required"}), 400) if recipe_uuid is None: return Response(json.dumps({"message": "Recipe UUID is required"}), 400) if user_token is None: return Response(json.dumps({"message": "User token is required"}), 400) # Get user from token user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return Response(json.dumps({"message": "User token is invalid"}), 400) # Get recipe entry from datastore recipe_dict = {} query = datastore.get_client().query(kind="Recipes") query.add_filter("recipe_uuid", "=", recipe_uuid) results = list(query.fetch()) # Verify recipe entry exists if len(results) < 0: return Response(json.dumps({"message": "Recipe uuid is invalid"}), 400) # Get recipe versions # NOTE: Versioning should not be done like this... device_software_version = datastore.get_device_software_version(device_uuid) versioned_recipe = results[0].get(f"recipe_v{device_software_version}") unversioned_recipe = results[0].get("recipe") # Get recipe dict if versioned_recipe is not None: recipe_dict = json.loads(versioned_recipe) else: recipe_dict = json.loads(unversioned_recipe) # Send recipe to device try: iot.send_start_recipe_command(device_uuid, recipe_uuid, recipe_dict) return Response(json.dumps({"message": "Sent recipe to device"}), 200) except iot.SendCommandError as e: print(f"Unable to send recipe to device: {e.message}") return Response(json.dumps({"message": e.message}), 503)
def submit_recipe(): """Save a recipe created or modified in the editor. .. :quickref: Recipe; Save recipe :reqheader Accept: application/json :<json string user_token: User's Token. :<json string recipe: JSON recipe. :<json string shared: 'true' if this is a shared recipe. **Example response**: .. sourcecode:: json { "message": "success", "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get("user_token", "") recipe_json = received_form_response.get("recipe") shared = received_form_response.get("shared", 'false') testing = received_form_response.get("testing") print(f'recipe_json: {recipe_json}') if user_token is None or recipe_json is None: return error_response( message="Please make sure you have added values for all the fields" ) # Get user uuid associated with this sesssion token user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response( message="Invalid User: Unauthorized" ) # if pytest is calling this, don't actually save a recipe if testing: return success_response(message="test worked") # Make sure this recipe has a UUID, it could be null meaning a new recipe. current_ts = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S:%f')[:-4] + 'Z' recipe_dict = json.loads(recipe_json) # json > dict is_new = False if recipe_dict.get('uuid') is None: # if no uuid, make one is_new = True new_uuid = str(uuid.uuid4()) recipe_dict["uuid"] = new_uuid recipe_dict["creation_timestamp_utc"] = current_ts if 0 < len(recipe_dict["authors"]): recipe_dict["authors"][0]["uuid"] = user_uuid recipe_json = json.dumps(recipe_dict) # dict > json # put a new recipe into the collection if is_new: entity = datastore.get_client().key('Recipes') recipe_reg_task = gcds.Entity(entity, exclude_from_indexes=["recipe"]) recipe_reg_task.update({ "recipe_uuid": recipe_dict.get("uuid"), "user_uuid": user_uuid, "recipe": recipe_json, "date_created": current_ts, "device_type": "PFC_EDU", "format": recipe_dict.get("format"), "version": recipe_dict.get("version"), "shared": shared, "image_url": recipe_dict.get("image_url"), }) datastore.get_client().put(recipe_reg_task) else: # recipe exists, so update it query = datastore.get_client().query(kind='Recipes') query.add_filter('recipe_uuid', '=', recipe_dict.get("uuid")) recipes = list(query.fetch(1)) if 0 == len(recipes): return error_response(message="No matching recipe in collection") recipe = recipes[0] recipe["recipe"] = recipe_json recipe["date_created"] = current_ts recipe["shared"] = shared datastore.get_client().put(recipe) return success_response( message="Successfully saved.", modified=current_ts, recipe_uuid=recipe_dict.get("uuid") )
def save_horticulture_measurements(): """Save horitculture measurements for a device. .. :quickref: Horticulture; Save horitculture measurements :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. :<json string device_uuid: Device UUID :<json string plant_height: plant_height :<json string leaf_count: leaf_count :<json string leaf_colors: leaf_colors :<json string leaf_withering: leaf_withering :<json string flavors: flavors :<json string root_colors: root_colors :<json string horticulture_notes: horticulture_notes :<json string submission_name: submission_name **Example response**: .. sourcecode:: json { "response_code": 200 } """ received_form_json = json.loads(request.data.decode('utf-8')) user_token = received_form_json.get("user_token") device_uuid = received_form_json.get("device_uuid") plant_height = received_form_json.get("plant_height", "") leaf_count = received_form_json.get("leaf_count", "") leaf_colors = received_form_json.get("leaf_colors", "") leaf_withering = received_form_json.get("leaf_withering", "") flavors = received_form_json.get("flavors", "") root_colors = received_form_json.get("root_colors", "") horticulture_notes = received_form_json.get("horticulture_notes", "") submission_name = received_form_json.get("submission_name", "") #print(received_form_json) if device_uuid is None: return error_response(message="Access denied.") # Add the user to the users kind of entity key = datastore.get_client().key('DailyHorticultureLog') # Indexes every other column except the description horticulture_task = gcds.Entity(key, exclude_from_indexes=[]) horticulture_task.update({ "device_uuid": device_uuid, "plant_height": str(plant_height), "leaf_count": str(leaf_count), "leaf_colors": ",".join(x for x in leaf_colors), "leaf_withering": ",".join(x for x in leaf_withering), "flavors": ",".join(x for x in flavors), "root_colors": ",".join(x for x in root_colors), "horticulture_notes": str(horticulture_notes), "submission_name": str(submission_name), "submitted_at": datetime.utcnow().isoformat().split('.')[0] + "Z", }) datastore.get_client().put(horticulture_task) if horticulture_task.key: return success_response() else: return error_response()
def get_all_recipes(): """Retrieve all recipes for a user account. .. :quickref: Recipe; Get all recipes :reqheader Accept: application/json :<json string user_token: User Token returned from the /login API. **Example response**: .. sourcecode:: json { "results": ["recipe", "recipe"], "devices": ["device", "device"], "user_uuid": "UUID-For-User", "response-code": 200 } **Example Recipe**: .. sourcecode:: json { "name": "Get Growing - Basil Recipe", "description": "Grows basil.", "recipe_uuid": "e6085be7-d496-43cc-8bd3-3a40a79e854e", "recipe_json": {"Recipe in": "JSON format"}, "user_uuid": "1e91ef7d-e9c2-4b0d-8904-f262a9eda70d", "image_url": "http://via.placeholder.com/200x200", "saved": true } **Example Device**: .. sourcecode:: json { "device_name": "Green-Frog-Bates", "device_notes": "", "device_reg_no": "F3D9051D", "device_type": "EDU", "device_uuid": "EDU-F3D9051D-b8-27-eb-0a-43-ee", "registration_date": "2019-04-08 13:18:58", "user_uuid": "d2c7fe68-e857-4c4a-98b4-7e88154ddaa6" } """ received_form_response = json.loads(request.data.decode('utf-8')) user_token = received_form_response.get("user_token", None) if user_token is None: return error_response( message="Please make sure you have added values for all the fields" ) user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response( message="Invalid User: Unauthorized" ) #Get all user devices query = datastore.get_client().query(kind='Devices') query.add_filter('user_uuid', '=', user_uuid) query_result = list(query.fetch()) results = list(query_result) devices_array = [] if len(results) > 0: for result_row in results: device_id = result_row.get("device_uuid", "") device_reg_no = result_row.get("device_reg_no", "") device_name = result_row.get("device_name", "") print(' {}, {}, {}'.format( device_id, device_reg_no, device_name)) result_json = { 'device_uuid': device_id, 'device_notes': result_row.get("device_notes", ""), 'device_type': result_row.get("device_type", ""), 'device_reg_no': device_reg_no, 'registration_date': result_row.get("registration_date", "").strftime("%Y-%m-%d %H:%M:%S"), 'user_uuid': result_row.get("user_uuid", ""), 'device_name': device_name } devices_array.append(result_json) # Get all the common AND shared AND user owned recipes. recipe_query = datastore.get_client().query(kind='Recipes') query.add_filter('user_uuid', '=', user_uuid) # users' recipes query.add_filter('user_uuid', '=', 'all') # AND 'all' common recipes query.add_filter('shared', '=', 'true') # AND shared recipes query_result = list(recipe_query.fetch()) results = list(query_result) results_array = [] for result in results: recipe_dict = json.loads(result["recipe"]) # Get the username who owns this recipe username = '' query = datastore.get_client().query(kind='Users') query.add_filter('user_uuid', '=', result['user_uuid']) user_list = list(query.fetch(1)) if 1 == len(user_list): user = user_list[0] username = user.get('username', '') results_array.append({ 'name': recipe_dict.get('name'), 'description': result.get('description', ''), 'recipe_uuid': result.get("recipe_uuid", ""), "recipe_json": recipe_dict, "user_uuid": result.get('user_uuid', ""), "image_url": result.get("image_url", ""), "shared": result.get("shared", ""), "username": username }) return success_response( results=results_array, devices=devices_array, user_uuid=user_uuid )
def register(): """Register a Food Computer and associate it with a user account. .. :quickref: Device; Register device :reqheader Accept: application/json :<json string user_token: User Token, to associate this device with :<json string device_name: User specified name for the device ('mine!') :<json string device_reg_no: Key from the device registration process :<json string device_notes: User specified notes about the device ('blue') :<json string device_type: PFC_EDU, FS, etc. **Example response**: .. sourcecode:: json { "response_code": 200 } """ received_form_response = json.loads(request.data.decode('utf-8')) print('register API received_form_response={}'.format( received_form_response)) user_token = received_form_response.get("user_token", None) device_name = received_form_response.get("device_name", None) device_reg_no = received_form_response.get("device_reg_no", None) device_notes = received_form_response.get("device_notes", None) device_type = received_form_response.get("device_type", None) testing = received_form_response.get("testing") time_stamp = datetime.now() if user_token is None or device_reg_no is None: return error_response( message="Please make sure you have added values for all the fields" ) if device_type is None: device_type = 'EDU' user_uuid = get_user_uuid_from_token(user_token) if user_uuid is None: return error_response(message="Invalid User: Unauthorized") if testing: # our pytest is hitting this API, so don't create the user return success_response() # Create a google IoT device registry entry for this device. # The method returns the device ID we need for IoT communications. try: device_uuid, device_software_version = \ iot.create_iot_device_registry_entry(device_reg_no, device_name, device_notes, device_type, user_uuid) except ValueError as e: return error_response(message=str(e)) except errors.HttpError as e: return error_response(message=e._get_reason()) if device_uuid is None: return error_response(message="Could not register this IoT device.") # Add the device to the Devices datastore collection key = datastore.get_client().key('Devices') device_reg_task = gcds.Entity(key, exclude_from_indexes=[]) device_reg_task.update({ 'device_uuid': device_uuid, 'device_name': device_name, 'device_reg_no': device_reg_no, 'device_notes': device_notes, 'user_uuid': user_uuid, 'device_type': device_type, 'registration_date': time_stamp, 'device_software_version': device_software_version }) datastore.get_client().put(device_reg_task) if device_reg_task.key: return success_response() else: return error_response(message="Sorry there was an error.")
def get_all_historical_values(device_uuid, start_timestamp, end_timestamp): print("Getting all historical values") co2 = [] temp = [] RH = [] leaf_count = [] plant_height = [] horticulture_notes = [] if device_uuid is None or device_uuid is "None": print(f"get_all_historical_values: No device_uuid") return temp, RH, co2, leaf_count, plant_height co2_vals = datastore.get_device_data(datastore.DS_co2_KEY, device_uuid, count=1000) temp_vals = datastore.get_device_data(datastore.DS_temp_KEY, device_uuid, count=1000) rh_vals = datastore.get_device_data(datastore.DS_rh_KEY, device_uuid, count=1000) if 0 == len(co2_vals) and 0 == len(temp_vals) and 0 == len(rh_vals): print(f"get_all_historical_values: No DeviceData for {device_uuid}") return temp, RH, co2, leaf_count, plant_height # handle None values for date range, in which case we return all start, end = None, None try: start = dt.strptime(start_timestamp, "%Y-%m-%dT%H:%M:%SZ") end = dt.strptime(end_timestamp, "%Y-%m-%dT%H:%M:%SZ") print( f"get_all_historical_values: using date range: {str(start)} to {str(end)}" ) except: start, end = None, None print(f"get_all_historical_values: no date range") # make sure the time column is the first entry in each dict for val in co2_vals: ts_str = utils.bytes_to_string(val["timestamp"]) ts = dt.strptime(ts_str, "%Y-%m-%dT%H:%M:%SZ") if start is not None and end is not None and (ts < start or ts > end): continue # this value is not in our start / end range value = utils.bytes_to_string(val["value"]) co2.append({"time": ts_str, "value": value}) for val in temp_vals: ts_str = utils.bytes_to_string(val["timestamp"]) ts = dt.strptime(ts_str, "%Y-%m-%dT%H:%M:%SZ") if start is not None and end is not None and (ts < start or ts > end): continue # this value is not in our start / end range value = utils.bytes_to_string(val["value"]) temp.append({"time": ts_str, "value": value}) for val in rh_vals: ts_str = utils.bytes_to_string(val["timestamp"]) ts = dt.strptime(ts_str, "%Y-%m-%dT%H:%M:%SZ") if start is not None and end is not None and (ts < start or ts > end): continue # this value is not in our start / end range value = utils.bytes_to_string(val["value"]) RH.append({"time": ts_str, "value": value}) # get horticulture measurements: leaf_count, plant_height query = datastore.get_client().query(kind="DailyHorticultureLog") query.add_filter("device_uuid", "=", device_uuid) query_result = list(query.fetch()) if 0 < len(query_result): for result in query_result: ts_str = str(utils.bytes_to_string(result["submitted_at"])) ts_str = ts_str.split(".")[0] try: ts = dt.strptime(ts_str, "%Y-%m-%dT%H:%M:%SZ") if start is not None and end is not None and (ts < start or ts > end): continue # this value is not in our start / end range if "leaf_count" in result: leaf_count.append({ "time": ts_str, "value": result["leaf_count"] }) if "plant_height" in result: plant_height.append({ "time": ts_str, "value": result["plant_height"] }) if "horticulture_notes" in result: horticulture_notes.append({ "time": ts_str, "value": result["horticulture_notes"] }) except: print("Invalid string format:", ts_str) continue return temp, RH, co2, leaf_count, plant_height, horticulture_notes