We can employ form flow to enable user interactions with the bot in a flow of prompts. Here is a great example for doing this for a simple "Order a sandwich" task. I want to know instead of command line prompts can I use real graphical interfaces? And instead of hard-coding the options can I pull the fields from a database or from some external resources through API calls?
None of the messenger channels (i.e. Facebook / Skype / Slack) support complex GUI elements yet (i.e. radio buttons, combo boxes, etc).
If you use the DynamicField elements, you can indeed populate the options from anywhere you like (including a database if you so wish):
.Field(new FieldReflector<BugReport>(nameof(Product))
.SetType(null)
.SetDefine((state, field) =>
{
foreach (var prod in GetProducts())
field
.AddDescription(prod, prod)
.AddTerms(prod, prod);
return Task.FromResult(true);
}))
Where GetProducts returns you a list of products - this could be from a DB, etc.
Sample taken from Dynamic FormFlow Forms in Bot Builder.
Related
I'm relatively new to web development in my current role and we have an MVC SPA type application I'm developing with my team. I can't really share any code directly as it's all the property of my company, so generally:
We're currently working to make a wizard that will collect several points of data and combine it all into the backend's SQL Stored Procs for inserting into tables. To start we've built a viewmodel that injects in and displays the possible options for various fields / drop downs. As choices are made in the drop downs it draws in new layers of the wizard.
To keep track of everything selected, I feel like our current strategy might be suboptimal - or more complex than needed.
When the View is interacted with, we're serializing the #Model into json, pushing it into the controller along with any data gathered from the user, de-serializing it into the ViewModel type, and sending it back in to the new partial view - storing any modifications from that action.
This keeps track of the changes as the user works through the wizard and allows us to redraw the primary div/page space to get new information as the context of what we're asking for changes.
I've seen websites do this with Forms or other things like modal pop ups, but as this is a multi step conditional wizard I chose against using that pathway as the views quickly became a mess of conditional razor checks and in page updates.
I know enough to know I'm not sure what best practices are in web development and I'm finding it difficult to extrapolate from various tutorials around the web into the complexity of this particular project.
I can give a generified example of code below, but hopefully the explanation above will trigger someone's brain to help short cut me to a cleaner way to do this.
public class XController : Controller
{
...
public XController(XRepository xRepository, IViewModelFactory viewModelFactory, Input input)
{
_xRepository = xRepository;
_viewModelFactory = viewModelFactory;
_input = input;
}
public IActionResult XIndex()
{
return PartialView(); //Draws an add/edit/delete set of buttons to begin the wizard. Assume the Add button is clicked leading to the next method.
}
public IActionResult DisplayAddJob()
{
//get list of task and connection types so user can select
_taskTypes = _xRepository.GetTaskTypes().ToList();
_connectionTypes = _xRepository.GetConnectionTypes().ToList();
_queryTypes = _xRepository.GetQueryTypes().ToList();
//build and return viewmodel so u can bind it to AddjobIndex dropdowns
XViewModel xvm = _viewModelFactory.Build(User.Identity, _taskTypes, _connectionTypes,_queryTypes, "Miscellaneous", _input.Test);=
return PartialView(xvm); //The initial view model creation / draw happens here
}
[ActionName("JobQueryWithPriorData")]
public IActionResult JobQuery(string vConnection, string vmJson)
{
xConnection vc = JsonConvert.DeserializeObject<xConnection>();
xViewModel xvm = JsonConvert.DeserializeObject<xViewModel>(vmJson);
xvm.SelectedConnectionId = (int)vc.ConnectionId; //we equip the view model with data that changed in the page interaction
return PartialView("~/Views/Miscellaneous/X/JobQuery.cshtml", xvm); //redraw the view in the main content pane with the updates.
}
...
}
This pattern exists all across the controller. An example of the .cshtml code #razor / javascript calls into the Controller are as follows:
Razor:
<td class="td-main-table" valign="middle">
<button type="button" class="y" onclick="SelectButtonClicked('#JsonConvert.SerializeObject(Model.ConnectionList.Where(x=>x.ConnectionId == ct.ConnectionId))','#JsonConvert.SerializeObject(Model)')">
</button>
</td>
Javascript:
function SelectButtonClicked(vConnection, viewmodeljson)
{
var functionalUrl = '/X/QueryToTableWithPriorData'
$.ajax({
url: functionalUrl,
type: 'POST',
data: {
vConnection:vConnection,
vmJson:viewmodeljson
},
success:
function (response) {
$('#displayDataPartialView').html(response);
}
});
}
This methodology is working. We're slowly, over the life of the wizard, able to grab the users selections and keep track of them. I'm just curious if there is a better way to do this, as I'm certain if there is I'm not aware of where to hunt for it. I believe in the old days people would just store stuff in ViewData objects, but I didn't really start web dev until .net core 2.1.
My team and I have full creative control over this application, it's internal and something I built from the ground up specifically for our team to put internal tools into, it's fairly lightweight currently and I'm willing to modify it to have a better foundation going forward.
Would it be better to store the user selections in a table tied to the users session and just recall the data at the end of the wizard on submit?
Are forms more flexible than I think they are?
Should I use modal windows instead of redrawing the page every time as a step through this?
Edit:
After a few days of research, while I'm not sure I have the best practices by any stretch, I do believe what we're doing is adequate. I've learned more about client vs server-side rendering and I believe my post came from not quite understanding the two well enough.
I believe the functionality I was expecting was "client-side" functionality from a very server side framework: the ability to step through a wizard's various forms without losing the data as the client goes through. This can be achieved with javascript/angular/react/blazor manipulating the view without going back to the server with a post/get.
In the world where I'm using minimal javascript, the strategy I'm using to turn the model into json in order to attach more data to it on the server side and resend it to the client for further modification SEEMS appropriate.
I still am interested to see if anyone has a best practice, so I'll leave the question open for a bit longer to see if anyone has anything. It's possible I should add a more client side layer to the solution as currently my thought was to try and maximize how much I leveraged the C# portion of web development. It seems like that might be my overall issue here.
Additionally, I spent some time looking into ViewComponents at a the suggestion of a commenter, Xinran. They are neat and do perform similarly to the PartialView methodology I'm using. I like how they work, but they produce the same exact scenario. They are mostly server-side tools, and I still, in the end, run into exactly the scenario that makes me wonder if this is the best way to handle a wizard with multiple steps that evolves off the user's previous choices.
I'm working on a server-side project with a database with 2 tables:
tbl_Search and tbl_Provide
Here is some more info:
tbl_Search = table for users that search for items.
tbl_Provide = table for users that provide items.
So obviously I have 2 methods: Search() and Provide().
By calling Provide(), the user and the item are registered in tbl_Provide.
By calling Search(), the user is registered in tbl_Search as well as searching for the item he's looking for in tbl_Provide.
Now, since those methods involve database access, I decided to run them asynchronously so they won't block the main thread.
Also, each user has a rank.
My problem:
2 users or more would like to have the same item. In such situation, the user with the highest rank should be the one to have it.
Therefor, once the user finds the item he was looking for, he can't simply take it as there might be another user with a higher rank who should have it.
I'm kind of lost here, not sure how to solve it.
Thanks in advance.
I'm developing a bot using Bot Framework, LUIS and ActionBinding.
In one of my intent handlers I call a new Dialog which has the methods StartAsync(IDialogContext context) and ReceiveMessageAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
As I understand it, the messages typed by the user while waiting (with context.Wait(ReceiveMessageAsync)) won't be sent to LUIS, right?
So if I need to understand what the user is saying without having to parse the string, which are my options? Could call the ILuisService.QueryAsync with the message.Text for each message be an option?
I want to be able to detect entities typed by the user so I can map them to missing fields. For example in this conversation:
User: I want to book a flight. // LUIS detects intent
Bot: Ok. Can you tell me more about your flight? // child dialog is called to handle the rest of the conversation
User: I want to go to Madrid.
Bot: To fly to Madrid you can choose between company A, B or C.
User: I want to go with A tomorrow night
Bot: Ok, searching for available tickets for tomorrow night in A...
In this case there are no initial entities when the intent is detected, but there could be, and in that case the bot would not ask for the already given information.
For my project, a simple Form with one to one question-answer is not enough. I also need to make more validations and confirmations on previously set parameters if the user wants to change one or more parameters (i.e., I need to go back to all parameters and check if the changed parameter affects them). For example:
User: Wait, I want to fly to Barcelona instead.
Bot: Company A does not fly to Barcelona. You can choose between C and D.
User: Ok I want to fly with C.
Bot: There are tickets available for tomorrow night in company C. Keep the flight for tomorrow night?
User: yes.
Any tips or guidance for best practices would help a lot.
Thanks in advance.
Edit:
With the Sub Action solution, where would my validators operate? On the FulfillAsync method? I'd need to validate and then send a question to the user and understand the reply he sent (parsing entities). Would that be possible inside a LuisAction?
I'd like to use the QueryValueFromLuisAsync but after looking at it, I'd need to pass the paramName, which is one of the action properties (if i'm not mistaken) and that is what I'm trying to avoid. I don't want to map one answer (i.e., message.Text) to one field, i want to map one answer to multiple fields.
Let's say i need to fill a model that has 6 properties. If the bot asks one question to the user and in his reply there are 3 entities I want to map those entities to 3 fields and only make questions about the remaining non mapped fields afterwards.
My first reaction to this is to avoid using a custom child dialog and go with SubActions and create your own validators if you want to have complex logic there or even override the IsValid method from the sub action.
However, if that's not a possibility, then I would consider reusing the QueryValueFromLuisAsync method, where the action should be the model you want to interact with. That function will end up calling LUIS and will try to assign the result or return another intent/action depending on the scenario. I would give this a try.
NOTE: Please excuse my English. It ain't my native language - I sometimes misspell or use wrong time
SITUATION DESCRIPTION
First, I will describe the situation.
I'm working on a Windows Phone 8 App ( WP8.0 w/ .NET 4.5 in C#/XAML ). This is my first WP8 App and there is not much people around me who can help me with it, so I'm learning by myself from tutorials on the net and a few books (in other words, I'm rookie with not so much experience).
There is "not-so-large" amount of data I'm gathering from a webservices (which I will describe later) and I decided to use Model-View-ViewModel (MVVM) to store the data provided by it.
MVVM Structure:
this is just my pseudodiagram of how the MVVM structure looks like, description is below it
MainViewModel
--------------
|
+ several properties (Username, Password, etc...)
|
+ Commands (loadData1, loadData2, flush, ... - implementations of ICommand)
|
+ ------ PersonalInfoModel
| -----------------
| + several properties (name, surname, phonenumber, etc...)
|
|
+ ------ DataGroup1Model
| ---------------
| +several properties
| +ObservableCollection<Item1> (roughly 0 - 50 items)
| +ObservableCollection<Item2> (roughly 0 - 5 items)
| +ObservableCollection<string> (roughly 0 - 5 items)
|
| Item1 Item2
| ----- -----
| +several properties +several other properties
|
|
+ ------ DataGroup2Model (similar to previous)
...et cetera...
I have MainViewModel object containing few properties and few Commands (implementing ICommand) and also several Models, that contain other properties and observablecollections.
The MainViewModel is not populated by data at once, but over time as the user browses the application and requests it. Mainly because there are more webservices to gather it from - some of the data must be gathered based on user-specified criteria (like from which date to which etc.)
I decided to create one "persistent" (not sure if it's the right word) ViewModel that will be present throughout the entire application, because I sometimes need to combine data from several models in the page
For example I need to always display some basic userinfo in "userInfoBar" that is always present on the page + i need some information from DataGroup1Model and some from DataGroup2Model (which are populated by data from two webservices and are used in more combinations then just this one).
Webservices
I'm using webservices to gather data to populate the viewModel. Specifically one webservice with 9 methods, not sure about how to describe it - every one method gives me data about something, which i store in the datagroupModels (sometimes two datagroupmodels, like personalinfoModel and Datagroup1Model).
The webservices have been provided to me, i can't change them, i can only use them.
THE "PROBLEM"
I'm trying to figure out a good way to give signals to my pages that something is done. For example login sequence:
I have a page containing two TextBoxes which values are Binded to MainViewModel properties UserName and Password and a Button binded to Command Login in MainViewModel.
The login Command just fires up "doLogin" method, which uses the username and password properties and give it as credentials to instance of webserviceclient autogenerated by Visual Studio when adding the webservice and the client executes one of the webservice methods (asynchronous method) - for example login.
When this method is completed, it raises the Completed event associated with it and provides Result object in its CompletedArguments.
In the completed event i copy the data from the Result object to the viewModel.
At this point, it would be nice to give some signal to the UI that the login task is done and it should navigate to another page.
How should I do it?
Is there some preferred way to do it?
P.S.: If you have suggestions about something described here, I will be glad to hear them. Please also bear in mind that I'm a rookie, so i sometimes need explanation for dummies or get stuck at something basic and/or stupid.
Usually you notify the UI from the view model by just changing your data. For example, you could maintain some general “state” in a State property that is bound to in the UI. The UI could then display different things depending on which value that property has. You usually do that with data triggers. If it is less complicated, you can also just have a boolean that will tell you IsDataLoaded or something, i.e. if you loaded the data from the web service already. Or even simpler, if you want to display the result in the view, you would just have a property Result which you bind to. As long as you haven’t loaded the data, the value is null and the view would display the login form. If the data is not null (i.e. data was loaded) it will just show the data instead.
As for navigation, usually you would have own view models for every page of the navigation. So if you start with a login screen, you would have a “login view model”, and if the next page is a data visualization, you would have a “data view model”. Usually this is done using data templates. Data templates are templates that are dependant on a type. You would have a “meta” view model which basically just has a slot for the currently displayed view model. And then for each possible type, you would define a data template that defines its view for the specific view model type. And then to navigate, all you do is change the current view model in the meta view model.
If the notification needs to to the same view / viewmodel the implementation of INotifyPropertyChanged to get notifications should be sufficient.
So typically you would be updating a Success Property which in turn is also invoking the property changed event.
Alternatively if this login notification needs to travel across modules. To get the state to say a notification to a different modules (modules that are not know at the time of implementation). A publish subscribe model could be used.
https://msdn.microsoft.com/en-us/magazine/dn745866.aspx shows how to use MVVM Ligths publish mechanism.
Prisms EventAggregator could also be explored.
I would suggest you to take a look at prism http://compositewpf.codeplex.com/
This allow you to publish events or aggregate commands all over your app and to easily switch beetween views.
You'll have to read quite a lot documentation but this is worthwhile, I couldn't imagine develop a strong mvvm app without it now
I'm working on an ASP.NET project where I need to split our current application into a Canada/USA/International System. This split will allow certain users to only view/modify the Data in Regions that they have permission for.
To avoid a maintenance nightmare in the future I've added a RegionID to the main tables of the application, including permissions. This should allow me to easily split everything up, and manage what people are allowed to see and what people aren't allowed to see. Based on the AD user, I can then save their default region which will save them hassle of seeing a Region Splash screen every time they try to access the application.
As I've never built a regional web application, this sounds fairly simple in my head, but before I dive completely in I was wondering if there is a "by the book" way to do this? Keeping in mind that I would rather not have to completely re-build this app as I do not have the time or resources to do so.
in my opinion the best way to achieve this is using the same kind of approach as when you have different languages or countries. probably you will need to develop a file defining the context of the user, languagecontext, countrycontext - region in your case -. You can achieve this using database persistence with an ID for each region in user - anonymous is ok too - profile table or using cookies to save details of regioncontext. then you can consume that information in your normal classes displaying information taking into consideration user region.
public Region RegionContext
{
get
{
if (this.user != null &&
this.user.Region!= null)
return this.user.Region;
var region= _region.GetAllRegions().FirstOrDefault();
return region;
}
set
{
if (this.user == null)
return;
this.user.region = value;
_user.UpdateUser(this.User);
}
}
where region has the ID and user has a fk to region.
brgds!