For my Firebase data structure, I want it to handle something like this:
A user can create a way of logging a thought. They can call it whatever they want. Mary Chen passes the parameter journal while Mr Owner wants to name it (pass parameter) log. Firebase saves these as their own tree naming it whatever parameter the user asked to name it.
Then another tree appears: whenLoggedIn + \(parameter). This tree saves a yyyy-mm-dd date to the corresponding post.
Example:
{
"uids": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": {
"friends": {
"IbTuUfmIeO0KOBr5Q4gAqD": true
},
"name": "Mary Chen",
"journal": {
"entry 1- trader joes": "went to store! :)",
"entry 2- ate sushi": "took out the garbage today then got free sushi from trader joes!!!"
},
"whenLoggedInJournal": {
"1": "1997-12-25",
"2": "2016-2-23"
}
},
"L8kBHaGBr5Q4gAqDOhFY29Okepm1": {
"friends": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": true
},
"name": "Mr Owner",
"journal": {
"log 1": "spotted some drunkard in my store",
"log 2": "drainage pipe clogged with tomatos, I suspect the drunkard",
"log 3": "did inventory check, 1 less sushi box, suspect the drunkard"
},
"whenLoggedInLog": {
"1": "1997-12-25",
"2": "2016-2-27",
"3": "2016-4-2"
}
}
}
}
I read through "Structuring data" on the Firebase guide, but I did not grasp how to add in trees at a time. I also want to achieve a flattened data set; would that be necessary for what I am doing?
The recommended Firebase data structure is to pull up each entity to its own top-level node. So in your case that would lead to:
{
"userNames": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": "Mary Chen",
"L8kBHaGBr5Q4gAqDOhFY29Okepm1": "Mr Owner"
},
"userFriends": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": {
"IbTuUfmIeO0KOBr5Q4gAqD": true
},
"L8kBHaGBr5Q4gAqDOhFY29Okepm1": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": true
}
},
"userJournals": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": {
"entry 1- trader joes": "went to store! :)",
"entry 2- ate sushi": "took out the garbage today then got free sushi from trader joes!!!"
},
"L8kBHaGBr5Q4gAqDOhFY29Okepm1": {
"log 1": "spotted some drunkard in my store",
"log 2": "drainage pipe clogged with tomatos, I suspect the drunkard",
"log 3": "did inventory check, 1 less sushi box, suspect the drunkard"
}
},
"whenLoggedInJournals": {
"D0Ez8edYhIbTuUfmIeO0KOq5xVB3": {
"1": "1997-12-25",
"2": "2016-2-23"
},
"L8kBHaGBr5Q4gAqDOhFY29Okepm1": {
"1": "1997-12-25",
"2": "2016-2-27",
"3": "2016-4-2"
}
}
}
This structure:
makes it easier to load parts of the user data, e.g. just the names
makes it easier to secure the data, e.g. make the user names public, their friend list viewable by themselves and their friends and the journals and log-in data only viewable by themselves
Related
I have a realtime db all setup and working. The data structure is very simple:
Item
some: info
some: other info
Item 2
some: info
some: other info
My rules are also super simple:
{
"rules": {
".read":"auth.uid != null",
".write":"auth.uid != null"
}
}
The issue (obviously) is that while I am forcing a user to be authenticated, that's all I am requiring and any user can access all the items in the db.
What I want is a way to limit a user to an item.
something like:
Item1
some: info
some: other info
user_1: auth.uid
user_2: auth.uid2
Item2
some: info
some: other info
user_1: auth.uid3
user_2: auth.uid4
I can store that data but I am not sure how to structure my rules to limit that.
My actual json looks like:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0
}
What I plan to implement is:
{
"annotations": {
"8df0309f-dc62-821e-dd65-f0ad46396937": {
"author": "1OXVKN3Y5Z-11",
"xfdf": "LONG STRING"
}
},
"complete": false,
"created_at": "2020-09-01T17:52:25.653Z",
"field_values": {
"field_name": {
"name": "copy",
"value": "TEsting",
"widgetID": "e61e3abf-7cdd-7d07-daec-6c3d3a55d667"
}
},
"stamp_count": 0,
"users": [ "CFX4I0PTM9-11", "CFX4I0PTM9-7"]
}
One I implement that json structure, how can I setup rules to support?
From reading your question and the comment thread I think your requirement is:
Allow a user to access an item if their UID is associated with that item.
In that case, you'll first need to ensure that the UIDs are in keys, as you can't search across multiple values, as your proposed users array would require. So you'd end up with:
"items": {
"item1": {
...
"users": {
"CFX4I0PTM9-11": true,
"CFX4I0PTM9-7": true
}
}
}
Now with this structure, you can ensure a user can only update items where their UID is in the users map with rules like this:
{
"rules": {
"items": {
"$itemid": {
".write": "data.child('users').child(auth.uid).exists()"
}
}
}
}
For reading the specific item you could use a similar rule. That will allow the user to read an item once they know its complete path, and when their UID is in the users map.
But you won't be able to query this structure, as you can only index on named properties. For more on this, and the alternative data structure to still implement you use-case, see Firebase query if child of child contains a value
It's hard to explain but in fact I m trying to code my own library with The Google Assistant Service.
me > "set a timer"
GA > "sure, how long"
me > "10 mn"
GA > "ok, timer is set" (1st response)
GA > "Sorry I can't help you" (2nd response)
The reaction is normal, because service don't support timer. I want to code my own timer, but no way to keep the first response and block the second. dialog_state_out.supplemental_display_text contain only the first one, but the audio core play all the data we have in audio_out.audio_data.
How to separe the 2 responses, I don't see disconnection on the data flow and only 1 request done.
The right way to do it is using custom device actions. You can create your own action that will trigger on a query like "set a timer", allowing you to handle custom logic and even support parameters within the query itself.
This page in the documentation explains how to set them up. You define an action package with your actions. Here's an action for "blinking":
"actions": [
{
"name": "com.example.actions.BlinkLight",
"availability": {
"deviceClasses": [
{
"assistantSdkDevice": {}
}
]
},
"intent": {
"name": "com.example.intents.BlinkLight",
"parameters": [
{
"name": "number",
"type": "SchemaOrg_Number"
},
{
"name": "speed",
"type": "Speed"
}
],
"trigger": {
"queryPatterns": [
"blink ($Speed:speed)? $SchemaOrg_Number:number times",
"blink $SchemaOrg_Number:number times ($Speed:speed)?"
]
}
},
"fulfillment": {
"staticFulfillment": {
"templatedResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Blinking $number times"
}
},
{
"deviceExecution": {
"command": "com.example.commands.BlinkLight",
"params": {
"speed": "$speed",
"number": "$number"
}
}
}
]
}
}
}
}
],
I am developing a speech recognition for a custom device using Google Assistant SDK. I am using Action SDK to create custom actions.
In my example the Google Assistant doesn't recognize actions in german language in case these actions are marked with "locale": "de" and the assistants language is set to german. I recognized that query patterns are understood clearly, but the event is not triggered. If everything is set to english the events are triggered.
action.json
{
"locale": "de",
"manifest": {
"displayName": "Blink Licht",
"invocationName": "Blink Licht",
"category": "PRODUCTIVITY"
},
"actions": [
{
"name": "com.acme.actions.blink_light",
"availability": {
"deviceClasses": [
{
"assistantSdkDevice": {}
}
]
},
"intent": {
"name": "com.acme.intents.blink_light",
"parameters": [
{
"name": "number",
"type": "SchemaOrg_Number"
},
{
"name": "light_target",
"type": "LightType"
}
],
"trigger": {
"queryPatterns": [
"lasse das $LightType:light_target $SchemaOrg_Number:number mal blinken"
]
}
},
"fulfillment": {
"staticFulfillment": {
"templatedResponse": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Das Licht $light_target.raw blinkt $number mal"
}
},
{
"deviceExecution": {
"command": "com.acme.commands.blink_light",
"params": {
"lightKey": "$light_target",
"number": "$number"
}
}
}
]
}
}
}
}
],
"types": [
{
"name": "$LightType",
"entities": [
{
"key": "LIGHT",
"synonyms": [
"Licht",
"LED",
"Glühbirne"]
}
]
}
]
}
hotword.py - snipped of event processing
def process_event(event, device_id):
"""Pretty prints events.
Prints all events that occur with two spaces between each new
conversation and a single space between turns of a conversation.
Args:
event(event.Event): The current event to process.
device_id(str): The device ID of the new instance.
"""
if event.type == EventType.ON_CONVERSATION_TURN_STARTED:
print()
print(event)
if (event.type == EventType.ON_CONVERSATION_TURN_FINISHED and
event.args and not event.args['with_follow_on_turn']):
print()
if event.type == EventType.ON_DEVICE_ACTION:
for command, params in process_device_actions(event, device_id):
print('Do command', command, 'with params', str(params)) #
if command == "com.acme.commands.blink_light":
number = int(params['number'])
for i in range(int(number)):
print('Device is blinking.')
Project language in action console is German:
enter image description here
To update and make the action available for testing I used "gaction CLI".
The question: Why is the event/command "com.acme.commands.blink_light" in hotword.py not triggered in case using german language?
Thanks in anticipation!
Here's how I solved this problem:
1. Go to your action on google console and pick the project you're having this trouble with.
2. In the 'Overview' section you'll see a window with the languages of your action on top, and at their right a 'Modify languages' in blue. Click it and then delete the langauge you're not using, english in this case.
At least that worked for me.
I'm building an app in Firebase and could use some advice. Here are the key points:
The app is for tracking time worked by employees.
Users are divided into teams, with each team having a manager.
As users clock in and out, the app tracks how long they work.
I'm stuck on a design problem. Here are the requirements:
Managers can read & write the time cards for all users on their team, while users can only read & write their own.
When users are viewing their own time cards, the UI needs to show most recent time cards sorted by timestamp.
When managers are viewing the time cards of everyone on their team, the UI needs to show most recent time cards sorted by timestamp.
Solving for all 3 requirements simultaneously is proving to be a challenge. My first hunch was to structure the time cards like this:
{
"timeCards": {
"-teamId_1": {
"-userId_1": {
"-timeCard_1": {
"startsAt: 1234567890123,
"endsAt": 1234567899999,
"duration": 2234567
},
"-timeCard_2": "..."
}
}
}
}
This solves the first two requirements easily:
Easy to write the permission rules such that managers have read/write for the entire team, but users can only read/write their own.
Easy to query a specific user's hours sorted by timestamp w/ db.ref('timeCards/${tid}/${uid}').orderByChild('startsAt')
Unfortunately, to solve the 3rd requirement with this schema requires some heavy lifting on the client. There is no way (that I'm aware of) to query Firebase for all of a team's time cards sorted by timestamp. So, I'd have to pull down all time cards for the team, then merge & sort them on the client.
I'm thinking this calls for denormalization, maybe a structure like this:
{
"timeCards": {
"byTeam": {
"-teamId_1": {
"-timeCard_1": {
"userId": "-userId_1",
"startsAt: 1234567890123,
"endsAt": 1234567899999,
"duration": 2234567
},
"-timeCard_2": "..."
}
},
"byUser": {
"-userId_1": {
"-timeCard_1": {
"teamId": "-teamId_1",
"startsAt: 1234567890123,
"endsAt": 1234567899999,
"duration": 2234567
},
"-timeCard_2": "..."
}
}
}
}
But this gets complicated on the server, requiring two Firebase functions, one watching timeCards/byTeam and one watching timeCards/byUser, that each mirror the records into the other collection.
I'm not sure how to avoid an infinite update loop in that situation, though. (Imagine updating timeCards/byTeam/-teamId_1/-timeCard_1, which fires the Firebase function and updates timeCards/byUser/..., which fires the Firebase function and updates timeCards/byTeam/..., etc.)
Am I missing an easy solution?
According to the Firebase docs good practice is to keep data as flat as possible. So it could look like this:
{
"users": {
"-userId_1": {
"name": "John"
},
"-userId_2": {
"name": "Ann"
}
},
"teams": {
"-teamId_1": {
"name": "Gladiators"
}
},
"timeCards": {
"-timeCard_1": {
"startsAt": 1234567890123,
"endsAt": 1234567899999,
"duration": 2234567,
"userId": "-userId_1",
"teamId": "-teamId_1"
},
"-timeCard_2": {
"startsAt": 1234567890123,
"endsAt": 1234567899999,
"duration": 2234567,
"userId": "-userId_2",
"teamId": "-teamId_1"
}
},
"usersInTeams": {
"-teamId_1": {
"-userId_1": true,
"-userId_2": true
}
},
"teamsInUsers": {
"-userId_1": {
"-teamId_1": true
},
"-userId_2": {
"-teamId_1": true
}
},
"timeCardsInUsers": {
"-userId_1": {
"-timeCard_1": true
},
"-userId_2": {
"-timeCard_2": true
}
},
"timeCardsInTeams": {
"-teamId_1": {
"-timeCard_1": true,
"-timeCard_2": true
}
}
}
Then you could write a Cloud Function that triggers when you write to "timeCards" (node: /timeCards/{pushId}) and adds timeCard ID to "timeCardsInUsers" (node: /timeCardsInUsers/{userId}) and "timeCardsInTeams" (node: /timeCardsInTeams/{teamId}).
It should resolve the looping problem because you write to different nodes.
So I've been looking into the surveymonkey v3 api documentation for formatting questions types. What I want to do is create a multiple choice question that has an "other" option, which if selected has a text field that a user can fill in to be more specific. Is there a way to accomplish this with the api?
You should be able to do that when creating/updating a question.
Example:
POST /v3/surveys/<id>/pages/<id>/questions
{
"family": "single_choice",
"subtype": "vertical",
"answers": {
"other": [{
"text": "Other (please specify)",
"is_answer_choice": true,
"num_lines": 1,
"num_chars": 50
}],
"choices": [
{
"text": "Apples"
},
{
"text": "Oranges"
},
{
"text": "Bananas"
}
]
},
"headings": [
{
"heading": "What is your favourite fruit?"
}
]
}
This is_answer_choice field seems to not be accepted currently. That is a bug, you can watch the docs to potentially get notified on updates, or try it again later.
Edit: This method should work now, give it a try and let me know if it solves your problem!