Passing in LUIS entities to bind to dialog state - botframework

Can somebody help me interpret what the heck this means from the bot framework documention:
You can also pass in LUIS entities to bind to the state. If the EntityRecommendation.Type is a path to a field in your C# class then the EntityRecommendation.Entity will be passed through the recognizer to bind to your field. Just like initial state, any step for filling in that field will be skipped.
When I call my dialog I pass in my LuisResult result Entities collection like so:
context.Call(new FormDialog<ItemSearch>( new ItemSearch(), ItemSearch.BuildForm, options: FormOptions.PromptInStart,entities:result.Entities), null);
Within those entities is at least one which maps in both name and type to a public property on my dialog however the state never gets filled. What am I missing?
TIA.

You can find an example of this in the PizzaOrderDialog. if you look at FormDialog implementation, it is using the entity.type to map the passed in entity recommendation to a step in the form. Then the detected entities will be provided as an input to that step of the form.
Here is an example of how form can skip the kind step based on the detected entities by Luis model in pizza form:
var entities = new List<EntityRecommendation>(result.Entities);
if (!entities.Any((entity) => entity.Type == "Kind"))
{
// Infer kind
foreach (var entity in result.Entities)
{
string kind = null;
switch (entity.Type)
{
case "Signature": kind = "Signature"; break;
case "GourmetDelite": kind = "Gourmet delite"; break;
case "Stuffed": kind = "stuffed"; break;
default:
if (entity.Type.StartsWith("BYO")) kind = "byo";
break;
}
if (kind != null)
{
entities.Add(new EntityRecommendation(type: "Kind") { Entity = kind });
break;
}
}
}
var pizzaForm = new FormDialog<PizzaOrder>(new PizzaOrder(), this.MakePizzaForm, FormOptions.PromptInStart, entities);

It also appears that there is an issue with passing Entities in. It seems to work if the property you are mapping to is a Enum (as per the PizzaBot sample). However if the public property is a string, it doesn't map. I'm not sure about other types.
See here https://github.com/Microsoft/BotBuilder/issues/151

Related

Is there a way to make a field in Dynamics CRM as one time entry field?

Basically, what I want is that the user should be able to select the dropdown and not be able to change it after making and saving the selection. So it will be a one-time entry field.
Below is a screenshot of the field I want to apply this property.
So this field has a Yes or No selection. And to make the business logic from failing I have to make it a one-time entry field only.
I looked up the form editor for possible things but couldn't find anything that would let me achieve this.
UPDATE #1
Below is my onload function:
function Form_onload() {
var formType = Xrm.Page.ui.getFormType();
var p = Xrm.Page.getAttribute("opportunityid");
--------------NEW CODE--------------------------------
if(formType ==2){ //form type 2 means the form is a saved form. form type 1 is new form.
alert(formType);
var myattribute = Xrm.Page.getAttribute("var_internal");
var myname = myattribute.getName();
if (Xrm.Page.getControl(myname) != null) {
//alert(myname);
Xrm.Page.getControl(myname).setDisabled(true);
}
}
--------------NEW CODE---------------------------
if (formType == 1 && p != null && p.getValue() != null) {
alert('Child Opportunities can only be created by clicking the Create Child Opportunity button in the Opportunity ribbon.');
window.top.close();
}
}
No code solution: I think you could use an Option set with a Yes/No option and a default of Unassigned Value. Then add that field to Field Level Security with "Allow Update" set to No.
When updating the FLS field permissions, be sure that the profile is associated with the organization "team" so that all users can see the field:
Arun already gave you an hint how to proceed, I just tried this req on one of my instance.
Create one extra field (dummy field) in my case I call it new_hasfieldbeenchanged1
This field will hold data when field is changed. Lock this field (always) and keep this field on form (but visibile =false)
Now you need 2 trigger Onload and OnSave. Below function will do your work. Let me know if this helps.
function onLoad(executionContext) {
debugger;
var formContext;
if (executionContext && executionContext.getFormContext()) {
formContext = executionContext.getFormContext();
//executionContext.getEventSource()
if (formContext.getAttribute("new_hasfieldbeenchanged1") && formContext.getAttribute("new_hasfieldbeenchanged1").getValue()!=null) {
if (formContext.getControl("new_twooptionfield")) {
formContext.getControl("new_twooptionfield").setDisabled(true);
}
}
}
}
function onSave(executionContext) {
debugger;
var formContext;
if (executionContext && executionContext.getFormContext()) {
formContext = executionContext.getFormContext();
//executionContext.getEventSource()
if(formContext.getAttribute("new_hasfieldbeenchanged1") && formContext.getAttribute("new_twooptionfield") && formContext.getAttribute("new_twooptionfield").getIsDirty()){
formContext.getAttribute("new_hasfieldbeenchanged1").setValue((new Date()).toString());
if (formContext.getControl("new_twooptionfield")) {
formContext.getControl("new_twooptionfield").setDisabled(true);
}
}
}
}
Due to environment-specific settings in my DEV, I was not able to reproduce what was suggested by #Eccountable. Although, his solution worked in other environments.
#AnkUser has a good answer as well but I was looking to shorten the code and make things as simple as possible.
My solution
I was able to handle this using Javascript on client-side. using the XRM toolbox.
In the XRM toolbox, I located the javascript for the opportunity and observed field changes in formType when the Opportunity was New and when the Opportunity was Existing. This variable (formType) was =1 when the Opportunity was New and =2 when it was Existing/Saved.
Using this piece of information I was able to leverage my Javascript as follows in Form_onload()
function Form_onload() {
if (formType == 2) {
var myattribute = Xrm.Page.getAttribute("internal");
var myname = myattribute.getName();
if (Xrm.Page.getControl(myname) != null) {
Xrm.Page.getControl(myname).setDisabled(true);
}
}
}
There’s no OOB configuration for such custom requirements, but we can apply some script logic.
Assuming this is a picklist with default null and not a two-option bool field, we can use onLoad form script to check if it has value & lock it. No need to have onChange function.
If it’s a bool field, then it’s hard to achieve. You have to track the initial value & changes made to implement the logic you want. Or through some unsupported code.

Microsoft Bot: Show options from database instead of enums

In the example bot implementations from Microsoft, they use enums to define options for dialog, as shown in the example below:
public enum LengthOptions { SixInch, FootLong };
public enum BreadOptions { NineGrainWheat, NineGrainHoneyOat, Italian, ItalianHerbsAndCheese, Flatbread };
Can we use a normal list to fetch the values from the database and display it as options?
Thanks
You can't do this out of the box, but you could subclass FormBuilderBase<T>, overriding various methods to build the Form using whatever datasource you prefer.
Edit:
You can find the base class and implementation of FormBuilder here: https://github.com/Microsoft/BotBuilder/blob/master/CSharp/Library/FormFlow/FormBuilder.cs
Basically, there are a mess of virtual methods that you can override to customize how you want to form to behave, but the main one is Build. In the default implementation, it iterates though the enums to create a list of Field, which are basically each step in you form. Instead of that, you can iterate through whatever data you have pulled from your database and create a new Field for each item. It may look something like this:
public override IForm<T> Build(Assembly resourceAssembly = null, string resourceName = null)
{
var list = GetListOfItemsFromDatabase();
foreach (var item in _list)
{
// FieldFromItem is an IField and will also need to be created
Field(new FieldFormItem<T>(item));
}
Confirm(new PromptAttribute(_form.Configuration.Template(TemplateUsage.Confirmation)));
}
return base.Build(resourceAssembly, resourceName);
}
I know its late but found myself struggling with the same and found that below would be the right solution for this.In your FormFlow class just add the Terms and Descriptions manually.From your example if we are talking about length options then change the type of LengthOptions to string add following code when you build the form.
return new FormBuilder<SandwichForm>()
.Field(new FieldReflector<SandwichForm>(nameof(LengthOptions))
.SetDefine(async (state, field) =>
{
// Call database and get options and iterate over the options
field
.AddDescription("SixInch","Six Inch")
.AddTerms("SixInch", "Six Inch")
.AddDescription("FootLong ","Foot Long")
.AddTerms("FootLong ", "Foot Long")
return true;
}))
.OnCompletion(completionDelegate)
.Build();

How can I create temporary records of Linq-To-Sql types without causing duplicate key problems?

I have code that generates records based on my DataGridView. These records are temporary because some of them already exist in the database.
Crop_Variety v = new Crop_Variety();
v.Type_ID = currentCropType.Type_ID;
v.Variety_ID = r.Cells[0].Value.ToString();
v.Description = r.Cells[1].Value.ToString();
v.Crop = currentCrop;
v.Crop_ID = currentCrop.Crop_ID;
Unfortunately in this little bit of code, because I say that v.Crop = currentCrop,
now currentCrop.Crop_Varieties includes this temporary record. And when I go to insert the records of this grid that are new, they have a reference to the same Crop record, and therefore these temporary records that do already exist in the database show up twice causing duplicate key errors when I submit.
I have a whole system for detecting what records need to be added and what need to be deleted based on what the user has done, but its getting gummed up by this relentless tracking of references.
Is there a way I can stop Linq-To-Sql from automatically adding these temporary records to its table collections?
I would suggest revisiting the code that populates DataGridView (grid) with records.
And then revisit the code that operates on items from a GridView, keeping in mind that you can grab bound item from a grid row using the following code:
public object GridSelectedItem
{
get
{
try
{
if (_grid == null || _grid.SelectedCells.Count < 1) return null;
DataGridViewCell cell = _grid.SelectedCells[0];
DataGridViewRow row = _grid.Rows[cell.RowIndex];
if (row.DataBoundItem == null) return null;
return row.DataBoundItem;
}
catch { }
return null;
}
}
It is also hard to understand the nature of Crop_Variety code that you have posted. As the Crop_Variety seems to be a subclass of Crop. This leads to problems when the Crop is not yet bound to database and potentially lead to problems when you're adding Crop_Variety to the context.
For this type of Form application I normally have List _dataList inside form class, then the main grid is bound to that list, through ObjectBindingList or another way. That way _dataList holds all data that needs to be persisted when needed (user clicked save).
When you assign an entity object reference you are creating a link between the two objects. Here you are doing that:
v.Crop = currentCrop;
There is only one way to avoid this: Modify the generated code or generate/write your own. I would never do this.
I think you will be better off by writing a custom DTO class instead of reusing the generated entities. I have done both approaches and I like the latter one far better.
Edit: Here is some sample generated code:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="RssFeed_RssFeedItem", Storage="_RssFeed", ThisKey="RssFeedID", OtherKey="ID", IsForeignKey=true, DeleteOnNull=true, DeleteRule="CASCADE")]
public RssFeed RssFeed
{
get
{
return this._RssFeed.Entity;
}
set
{
RssFeed previousValue = this._RssFeed.Entity;
if (((previousValue != value)
|| (this._RssFeed.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._RssFeed.Entity = null;
previousValue.RssFeedItems.Remove(this);
}
this._RssFeed.Entity = value;
if ((value != null))
{
value.RssFeedItems.Add(this);
this._RssFeedID = value.ID;
}
else
{
this._RssFeedID = default(int);
}
this.SendPropertyChanged("RssFeed");
}
}
}
As you can see the generated code is establishing the link by saying "value.RssFeedItems.Add(this);".
In case you have many entities for wich you would need many DTOs you could code-generate the DTO classes by using reflection.

Using DataObjectTypeName in DataObjectSource

The functionality I am trying to use is:
- Create a ObjectDataSource for selection and updating controls on a web page (User Control).
- Use the DataObjectTypeName to have an object created that would send the data to an UpdateMethod.
- Before the values are populated in the DataObjectTypeName’s object, I would like to pre-populate the object so the unused items in the class are not defaulted to zeros and empty strings without me knowing whether the zero or default string was set by the user or by the application.
I cannot find a way to pre-populate the values (this was an issue back in 2006 with framework 2.0). One might ask “Why would anyone need to pre-populate the object?”. The simple answer is: I want to be able to randomly place controls on different User Controls and not have to be concerned with which UpdateMethod needs to handle which fields of an object.
For Example, let’s say I have a class (that reflects a SQL Table) that includes the fields: FirstName, LastName, Address, City, State, Zip. I may want to give the user the option to change the FirstName and LastName and not even see the Address, City, State, Zip (or vice-versa). I do not want to create two UpdateMethods where one handled FirstName and LastName and the other method handles the other fields. I am working with a Class of some 40+ columns from multiple tables and I may want some fields on one screen and not another and decide later to change those fields from one screen to another (which breaks my UpdateMethods without me knowing).
I hope I explained my issue well enough.
Thanks
This is hardly a solution to the problem, but it's my best stab at it.
I have a GridView with its DataSourceID set to an ObjectDataSource.
Whenever a row is updated, I want the property values in the object to be selectively updated - that is - only updated if they appear as columns in the GridView.
I've created the following extension:
public static class GridViewExtensions
{
public static void EnableLimitUpdateToGridViewColumns(this GridView gridView)
{
_gridView = gridView;
if (_gridView.DataSourceObject != null)
{
((ObjectDataSource)_gridView.DataSourceObject)
.Updating += new ObjectDataSourceMethodEventHandler(objectDataSource_Updating);
}
}
private static GridView _gridView;
private static void objectDataSource_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
var newObject = ((object)e.InputParameters[0]);
var oldObjects = ((ObjectDataSource)_gridView.DataSourceObject).Select().Cast<object>();
Type type = oldObjects.First().GetType();
object oldObject = null;
foreach (var obj in oldObjects)
{
if (type.GetProperty(_gridView.DataKeyNames.First()).GetValue(obj, null).ToString() ==
type.GetProperty(_gridView.DataKeyNames.First()).GetValue(newObject, null).ToString())
{
oldObject = obj;
break;
}
}
if (oldObject == null) return;
var dynamicColumns = _gridView.Columns.OfType<DynamicField>();
foreach (var property in type.GetProperties())
{
if (dynamicColumns.Where(c => c.DataField == property.Name).Count() == 0)
{
property.SetValue(newObject, property.GetValue(oldObject, null), null);
}
}
}
}
And in the Page_Init event of my page, I apply it to the GridView, like so:
protected void Page_Init()
{
GridView1.EnableLimitUpdateToGridViewColumns();
}
This is working well for me at the moment.
You could probably apply similar logic to other controls, e.g. ListView or DetailsView.
I'm currently scratching my head to think of a way this can be done in a rendering-agnostic manner - i.e. without having to know about the rendering control being used.
I hope this ends up as a normal feature of the GridView or ObjectDataSource control rather than having to hack it.

How do you model form changes under Spring MVC?

Say you're writing a web page for fruit vendors using Spring MVC's SimpleFormController, version 2.5.6. On this page the vendor can do simple things like change their name or their address. They can also change their inventory based on a drop down list filled with present inventory selections.
When this drop down list selection changes, the entire form changes to match the inventory of what has been selected. So one stock selection may have bananas and pears, another may have melons, blueberries and grapefruit.
Inside each inventory selection is a input field that needs to be propagated back to the database, for the sake of this example let's say that the user enters the number of fruit.
The way this is modeled in the database is that each Stock name is stored in a table, which has a one to many relationship with the contents of each stock, which would be the type of fruit in this example. Then the type of fruit has a one to many relationship with the quantity the vendor selects. Stock name and the type of fruit in each stock are stored in the database and are unchangeable by the user, with the connected fruit quantity table being editable.
My question is, how do you model the form described above in Spring MVC?
I've tried overriding the isFormChangeRequest and onFormChange to facilitate the form change, but I think I may be misunderstanding the intent of these methods. When I change my backing command object the next time the page is post it tries to bind the request into the form, which breaks if you adjust the size of the Stock array (say from 3 to 2, it will try and bind into the 3rd value, even if it is empty).
If you have a limited amount of different stocks, you can use different handler mappings for each one with a different backing model:
#RequestMapping(params="stock=example1")
ModelAndView handleExample1(#ModelAttribute("stock") ApplesOrangesPears stockObject)
#RequestMapping(params="stock=example2")
ModelAndView handleExample2(#ModelAttribute("stock") BananasPotatos stockObject)
But I guess that is not the case, there are a lot of different stock types and they are dynamic. In that case you can register custom property editor (#InitBinder), and determine dynamically the actual type of the backing object for the inventory, then validate, and convert to or from it explicitly.
What I ended up doing is firing a JavaScript event when the selection in the drop down is changed. This JavaScript (seen below) generates a URL based on the selection of the drop down and uses a location.replace to go to the new URL, which causes the controller to generate a new form.
Using this method over overriding the isFormChangeRequest and onFormChange has allowed me to avoid binding errors caused by left over post data.
function changeUrl(selectionValue) {
var param = getParams();
param["dropdownselection"] = selectionValue;
window.location.replace(getBaseUrl() + buildQueryString(param));
}
//taken from http://javascript.about.com/library/blqs1.htm
function getParams() {
var qsParm = new Array();
var query = window.location.search.substring(1);
var parms = query.split('&');
for (var i = 0; i < parms.length; i++) {
var pos = parms[i].indexOf('=');
if (pos > 0) {
var key = parms[i].substring(0,pos);
var val = parms[i].substring(pos+1);
qsParm[key] = val;
}
}
return qsParm;
}
function getBaseUrl() {
var url = document.location.toString();
if (url.indexOf('?') != -1) {
url = url.substring(0, url.indexOf('?'));
}
return url;
}
function buildQueryString(param) {
var queryString = "?";
for (var key in param) {
queryString += key + "=" + param[key] + "&";
}
//remove last "&"
return queryString.substring(0,queryString.length - 1);
}

Resources