Query criteria: from public variable or from form - forms

After a few hours searching, I couldn't find any solution to this little problem I'm having.
I have a query that retrieves one of its criteria from a form. I have referenced correctly the value on the form from the query, and it works, but what I wanted to do is a bit more complicated: when the form is closed, I want to launch the query with a "default value".
I tried to do it in 2 different ways:
a) Defining an "IIf" at the query criteria: I would need a function that checks if the form from which I retrieve the values is open.
b) Defining public variables with a default value, which would be changed from the form: I don't know where/when to initialize the value of the variable.
Does anyone have a better idea on how to do this?
TL;DR: Query gets criteria from form when it's open. If form is closed, query uses default value. HELP!

You can create a VBA function in a module to do this :
Function MyCriterion() As Long
MyCriterion = 1234 ' default value
If CurrentProject.AllForms("MyForm").IsLoaded Then
MyCriterion = Forms("MyForm").MyControl.Value
End If
End Function

Related

bindFromRequest and asFormUrlEncoded return different values

I have a form where a field name is the same as one of the method/url parameters on the submit, say someInt. I.e. my form has #(dummyForm:Form[Dummy], someInt:Int) and dummyForm has a field "someInt" -> number and the controller is defined as def submit(someInt:Int) =.... Sample code here.
Let's say I submit the form with dummy.someInt value 222 and url parameter 555, I find the following:
request.body.asFormUrlEncoded shows one someInt, namely the value entered in the input field: (someInt,ArrayBuffer(222))
bindFromRequest, however somehow binds the form value to the url parameter value, 555 in this case
Is this expected behaviour? I would have thought bindFromRequest would be able to differentiate between the two? Is there a preferred way to prevent this type of conflict (besides having different names)?
(There is a workaround in this case. Instead of using the parameterless version of bindFromRequest, it seems to work as desired if you explicitly specify the asFormUrlEncoded set of values, i.e. bindFromRequest(request.body.asFormUrlEncoded.getOrElse(Map())). I am using Scala - have not tried to replicate in Java.)
In the bindFromRequest function, request.queryString is explicitly append to the list of values.

Passing arguments to Access Forms created with 'New'

I have a form called 'detail' which shows a detailed view of a selected record. The record is selected from a different form called 'search'. Because I want to be able to open multiple instances of 'detail', each showing details of a different record, I used the following code:
Public detailCollection As New Collection
Function openDetail(patID As Integer, pName As String)
'Purpose: Open an independent instance of form
Dim frm As Form
Debug.Print "ID: " & patID
'Open a new instance, show it, and set a caption.
Set frm = New Form_detail
frm.Visible = True
frm.Caption = pName
detailCollection.Add Item:=frm, Key:=CStr(frm.Hwnd)
Set frm = Nothing
End Function
PatID is the Primary Key of the record I wish to show in this new instance of 'detail.' The debug print line prints out the correct PatID, so i have it available. How do I pass it to this new instance of the form?
I tried to set the OpenArgs of the new form, but I get an error stating that OpenArgs is read only. After researching, OpenArgs can only be set by DoCmd (which won't work, because then I don't get independent instances of the form). I can find no documentation on the allowable parameters when creating a Form object. Apparently, Microsoft doesn't consider a Constructor to be a Method, at least according to the docs. How should I handle this? (plz don't tell me to set it to an invisible text box or something) Thanks guys, you guys are the best on the net at answering these questions for me. I love you all!
Source Code for the multi-instance form taken from: http://allenbrowne.com/ser-35.html
Inside your Form_detail, create a custom property.
Private mItemId As Long
Property Let ItemID(value as Long)
mItemId = value
' some code to re query Me
End Property
Property Get ItemId() As Long
ItemId = mItemId
End Property
Then, in the code that creates the form, you can do this.
Set frm = New Form_detail
frm.ItemId = patId
frm.Visible = True
frm.Caption = pName
This will allow you to pass an ID to the new form instance, and ensure it gets requeried before making it visible. No need to load all of the results every time if you're always opening the form by Newing it. You let the property load the data instead of the traditional Form_Load event.
This works because Access Form modules are nothing more than glorified classes. Hope this helps.
You could try applying a filter:
frm.Filter = "[ID] = " & patID
frm.FilterOn = True
The Record Source of the Detail form will need to be set to the table to which the ID belongs.
UPDATE
As you requested, here is the code to set the RecordSource:
frm.RecordSource = "select * from TableName where [ID] = " & patID
This is probably cleaner than using a filter given that a user can remove the filter (depending on the type of form).

BIRT report parameter multiple selection value "All"

I have a problem with creating default value of 'All values' for a cascading parameter group last parameter. Actually I don't neccesary need that value to be default, but that would be preferable.
I have tried where I create additional data set with the needed value and additional data set with value All which uses different scripted data source, and another data set with computed column with full outer join, that column uses this code
if(row["userName"]==null ){
row["All"];
}else{
row["userName"];
}
and in the last cascaded parameter JDSuser which I need that All value I have added default value (All users).
In the data set with one value All in open I have script
ii=0;
in fetch
if( ii > 0 ){
return false;
}else{
row["All"] = "(All Users)";
ii++
return true;
}
and in the query data set, in beforeOpen script in if statement I have
if( params["JDSuser"].value!=null && params["JDSuser"].value[0] != "(All Users)" ){
This is used if I haven't selected All users value, and this works, though if I select All Users, it retrieves me no data.
I'm creating from this source example actuate link for example rptdesign download
If someone could give me some help, I would be very grateful.
The way you generate "(All values)" item in your selection list seems to me over complicated but if i understood correctly your case this part is working fine, the problem is not in the definition of the cascading parameter but the way it is used in the main dataset of the report.
Furthermore we have to assume we speak about the same query & beforeOpen script involved in this topic. No data are returned because if we don't do anything special when this item "All values" has been selected, then those filters are still active:
and role.name in ( 'sample grupa' )
and userbase.userName in ( 'sample' )
There are a couple of options to handle this. An elegant one is to declare a dataset parameter linked to your report parameter "JDSuser", and use a clause "OR" such:
and role.name in ( 'sample grupa' )
and (?='(All users)' OR userbase.userName in ( 'sample' ))
Notice this question mark, which represents a dataset parameter in your query. It is not intrusive: the beforeOpen script doesn't have to be changed. You probably need to do something similar with the other filter role.name, but you don't provide any information related to this. One more thing, in order to avoid bad surpises may be you should choose as value something more simple without brackets such "_allitems", and set "(All items)" as label.
Please refer to this topic for more informations about handling optional parameters. See a live example of optional parameters in a cascading group here.

Text input through SSRS parameter including a Field name

I have a SSRS "statement" type report that has general layout of text boxes and tables. For the main text box I want to let the user supply the value as a parameter so the text can be customized, i.e.
Parameters!MainText.Value = "Dear Mr.Doe, Here is your statement."
then I can set the text box value to be the value of the parameter:
=Parameters!MainText.Value
However, I need to be able to allow the incoming parameter value to include a dataset field, like so:
Parameters!MainText.Value = "Dear Mr.Doe, Here is your [Fields!RunDate.Value] statement"
so that my report output would look like:
"Dear Mr.Doe, Here is your November statement."
I know that you can define it to do this in the text box by supplying the static text and the field request, but I need SSRS to recognize that inside the parameter string there is a field request that needs to be escaped and bound.
Does anyone have any ideas for this? I am using SSRS 2008R2
Have you tried concatenating?
Parameters!MainText.Value = "Dear Mr.Doe, Here is your" & [Fields!RunDate.Value] & "statement"
There are a few dramatically different approaches. To know which is best for you will require more information:
Embedded code in the report. Probably the quickest to
implement would be embedded code in the report that returned the
parameter, but called String.Replace() appropriately to substitute
in dynamic values. You'll need to establish some code for the user for which strings will be replaced. Embedded code will get you access to many objects in the report. For example:
Public Function TestGlobals(ByVal s As String) As String
Return Report.Globals.ExecutionTime.ToString
End Function
will return the execution time. Other methods of accessing parameters for the report are shown here.
1.5 If this function is getting very large, look at using a custom assembly. Then you can have a better authoring experience with Visual Studio
Modify the XML. Depending on where you use
this, you could directly modify the .rdl/.rdlc XML.
Consider other tools, such as ReportBuilder. IF you need to give the user
more flexibility over report authoring, there are many tools built
specifically for this purpose, such as SSRS's Report Builder.
Here's another approach: Display the parameter string with the dataset value already filled in.
To do so: create a parameter named RunDate for example and set Default value to "get values from a query" and select the first dataset and value field (RunDate). Now the parameter will hold the RunDate field and you can use it elsewhere. Make this parameter hidden or internal and set the correct data type. e.g. Date/Time so you can format its value later.
Now create the second parameter which will hold the default text you want:
Parameters!MainText.Value = "Dear Mr.Doe, Here is your [Parameters!RunDate.Value] statement"
Not sure if this syntax works but you get the idea. You can also do formatting here e.g. only the month of a Datetime:
="Dear Mr.Doe, Here is your " & Format(Parameters!RunDate.Value, "MMMM") & " statement"
This approach uses only built-in methods and avoids the need for a parser so the user doesn't have to learn the syntax for it.
There is of course one drawback: the user has complete control over the parameter contents and can supply a value that doesn't match the report content - but that is also the case with the String Replace method.
And just for the sake of completeness there's also the simplistic option: append multiple parameters: create 2 parameters named MainTextBeforeRunDate and MainTextAfterRunDate.
The Textbox value expression becomes:
=Parameters!MainTextBeforeRunDate.Value & Fields!RunDate.Value & Parameters!MainTextAfterRunDate.Value.
This should explain itself. The simplest solution is often the best, but in this case I have my doubts. At least this makes sure your RunDate ends up in the final report text.

How do I dynamically build a search block in sunspot?

I am converting a Rails app from using acts_as_solr to sunspot.
The app uses the field search capability in solr that was exposed in acts_as_solr. You could give it a query string like this:
title:"The thing to search"
and it would search for that string in the title field.
In converting to sunspot I am parsing out field specific portions of the query string and I need to dynamically generate the search block. Something like this:
Sunspot.search(table_clazz) do
keywords(first_string, :fields => :title)
keywords(second_string, :fields => :description)
...
paginate(:page => page, :per_page => per_page)
end
This is complicated by also needing to do duration (seconds, integer) ranges and negation if the query requires it.
On the current system users can search for something in the title, excluding records with something else in another field and scoping by duration.
In a nutshell, how do I generate these blocks dynamically?
I recently did this kind of thing using instance_eval to evaluate procs (created elsewhere) in the context of the Sunspot search block.
The advantage is that these procs can be created anywhere in your application yet you can write them with the same syntax as if you were inside a sunspot search block.
Here's a quick example to get you started for your particular case:
def build_sunspot_query(conditions)
condition_procs = conditions.map{|c| build_condition c}
Sunspot.search(table_clazz) do
condition_procs.each{|c| instance_eval &c}
paginate(:page => page, :per_page => per_page)
end
end
def build_condition(condition)
Proc.new do
# write this code as if it was inside the sunspot search block
keywords condition['words'], :fields => condition[:field].to_sym
end
end
conditions = [{words: "tasty pizza", field: "title"},
{words: "cheap", field: "description"}]
build_sunspot_query conditions
By the way, if you need to, you can even instance_eval a proc inside of another proc (in my case I composed arbitrarily-nested 'and'/'or' conditions).
Sunspot provides a method called Sunspot.new_search which lets you build the search conditions incrementally and execute it on demand.
An example provided by the Sunspot's source code:
search = Sunspot.new_search do
with(:blog_id, 1)
end
search.build do
keywords('some keywords')
end
search.build do
order_by(:published_at, :desc)
end
search.execute
# This is equivalent to:
Sunspot.search do
with(:blog_id, 1)
keywords('some keywords')
order_by(:published_at, :desc)
end
With this flexibility, you should be able to build your query dynamically. Also, you can extract common conditions to a method, like so:
def blog_facets
lambda { |s|
s.facet(:published_year)
s.facet(:author)
}
end
search = Sunspot.new_search(Blog)
search.build(&blog_facets)
search.execute
I have solved this myself. The solution I used was to compiled the required scopes as strings, concatenate them, and then eval them inside the search block.
This required a separate query builder library that interrogates the solr indexes to ensure that a scope is not created for a non existent index field.
The code is very specific to my project, and too long to post in full, but this is what I do:
1. Split the search terms
this gives me an array of the terms or terms plus fields:
['field:term', 'non field terms']
2. This is passed to the query builder.
The builder converts the array to scopes, based on what indexes are available. This method is an example that takes the model class, field and value and returns the scope if the field is indexed.
def convert_text_query_to_search_scope(model_clazz, field, value)
if field_is_indexed?(model_clazz, field)
escaped_value = value.gsub(/'/, "\\\\'")
"keywords('#{escaped_value}', :fields => [:#{field}])"
else
""
end
end
3. Join all the scopes
The generated scopes are joined join("\n") and that is evaled.
This approach allows the user to selected the models they want to search, and optionally to do field specific searching. The system will then only search the models with any specified fields (or common fields), ignoring the rest.
The method to check if the field is indexed is:
# based on http://blog.locomotivellc.com/post/6321969631/sunspot-introspection
def field_is_indexed?(model_clazz, field)
# first part returns an array of all indexed fields - text and other types - plus ':class'
Sunspot::Setup.for(model_clazz).all_field_factories.map(&:name).include?(field.to_sym)
end
And if anyone needs it, a check for sortability:
def field_is_sortable?(classes_to_check, field)
if field.present?
classes_to_check.each do |table_clazz|
return false if ! Sunspot::Setup.for(table_clazz).field_factories.map(&:name).include?(field.to_sym)
end
return true
end
false
end