How to add facts to JESS working memory when a event happend out of Jess - rule-engine

How can I do to add new dynamic facts to working memory?
for Example:
If I have a rule like this:
(deftemplate person(slot idPerson)(slot age)(slot like))
(defrule sports
"Give a discount on sport items"
(person {age >18 && age<30}{like soccer}{idPerson ?idPerson})
=>
(add (new offer"10% of discount on sport items to" ?idPerson)))
And.. If Jess is running until there are rule to fire.(run-until-halt)
How can I do to add new facts to Jess working memory from another
application or script when a event happend out of Jess?

Related

Bundle products not added into cart in magento 2

I create a bundle products with many group products inside bundle product, where user can also change the qty of group product inside bundle, all group products have qty more than 10 ,but the problem is that its not adding to cart.
Error shows screenshot
It was a caching/SID issue!
Symptoms to reproduce: Magento Blocks HTML caching is turned on (only when turned on)
A SID parameter was being added to the URL that I couldn't see and was being added in between query parameters in a way that messed everything up.
How to fix, add '_nosid' => true to URL params:
$params = array('_query'=> $query,'qty' => 1, '_nosid' => true);
Then the add to cart URL can be retrieved by
Mage::helper('checkout/cart')->getAddUrl($_product, $params)

How can I create and update pages dynamically in Sulu CMS?

I have the following situation:
A database stores information about houses (address, number of rooms, date built, last selling price, etc.)
This database is being manipulated through an app (let's call that app the "backend house app") that cannot be directly integrated in a Sulu-driven app. I can access the stored data through an API that gives me JSON-representations of House-objects. I can also have the app launch some sort of call to a Sulu-driven app when a house is created, updated or deleted.
The Sulu-driven app (let's call that the "frontend house app") with templates for "house", "room", etc., is connected to a different database on a different server. This Sulu-driven app's website-environment shows house-pages with room-pages where some content is pre-filled through a connection to the "backend house app". Other content only exists on the database of the "frontend house app", like user comments, appraisals of interior design, etc., according to configured aspects of the Sulu-templates.
What I want to achieve, is a way to automate the creation, updating and deletion of "frontend house app"-pages based on activity in the "backend house app".
For instance, when a new house is added in the "backend house app", I want it to notify the "frontend house app" so that the "frontend house app" will automatically create the entire node-tree for the newly added house. Meaning: a "house"-page with the required data filled in, "room"-pages for each room, etc., so that the content manager of the "frontend house app" can see the entire tree of the newly added house in the workspace and can start manipulating content in the already available templates. In addition to automatically creating these pages, I also want to pre-set the rights to update and create, since the content manager of the "frontend house app" must not be able to create new rooms or change the name of the house, for instance.
I did not manage to get it working, I'll just add what I already done to show where I got stuck.
I started out with the following code, in a controller that extends Sulu's own WebsiteController:
$documentManager = $this->get('sulu_document_manager.document_manager');
$nodeManager = $this->get('sulu_document_manager.node_manager');
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
$newHouseDocument = $documentManager->create('page');
// The backendApi just gives a House object with data from the backend
// In this case we get an existing House with id 1
$house = $backendApi->getHouseWithId(1);
$newHouseDocument->setTitle($house->getName()); // For instance 'Smurfhouse'
$newHouseDocument->setLocale('nl'); // Nl is the only locale we have
$newHouseDocument->setParent($parentHouseDocument); // A default page where all the houses are listed
$newHouseDocument->setStructureType('house'); // Since we have a house.xml template
// I need to grab the structure to fill it with values from the House object
$structure = $newHouseDocument->getStructure();
$structure->bind([
'title' => $house->getName(),
'houseId' => $house->getId(),
]);
$newHouseDocument->setWorkflowStage(WorkflowStage::PUBLISHED); // You would expect this to automatically publish the document, but apparently it doesn't... I took it from a test I reverse-engineered in trying to create a page, I have no clue what it is supposed to change.
$nodeManager->createPath('/cmf/immo/routes/nl/huizen/' . $house->getId());
$documentManager->persist(
$newHouseDocument,
'nl',
[
'path' => '/cmf/immo/contents/huizen/' . Slugifier::slugify($house->getName()), // Assume for argument's sake that the Slugifier just slugifies the name...
'auto_create' => true, // Took this value from a test that creates pages, don't know whether it is necessary
'load_ghost_content' => false, // Idem
]
);
$documentManager->flush();
Now, when I fire the controller action, I first get the exception
Property "url" in structure "house" is required but no value was given.
I tried to fix this by just manually binding the property 'url' with value '/huizen/' . $house->getId() to $structure, at the point where I bind the other values. But this doesn't fix it, as apparently the url value is overwritten somewhere in the persist event chain, and I haven't yet found where.
However, I can, just for testing purposes, manually override the url in the StructureSubscriber that handles the mapping for this particular persist event. If I do this, something gets created in the Sulu-app-database - hurray!
My phpcr_nodes table lists two extra records, one for the RouteDocument referring to /cmf/immo/routes/nl/huizen/1, and one for the PageDocument referring to /cmf/immo/contents/huizen/smurfhouse. Both have the workspace_name column filled with the value default_live. However, as long as there are not also records that are complete duplicates of these two records except with the value default in the workspace_name column, the pages will not appear in the Sulu admin CMS environment. Needless to say, they will also not appear on the public website proper.
Furthermore, when I let the DocumentManager in my controller action try to ->find my newly created document, I get a document of the class UnknownDocument. Hence, I cannot have the DocumentManager go ->publish on it; an Exception ensues. If I visit the pages in the Sulu admin environment, they are hence unpublished; once I publish them there, they can be found by the DocumentManager in the controller action - even if I later unpublish them. They are no longer UnknownDocument, for some reason. However, even if they can be found, I cannot have the DocumentManager go ->unpublish nor ->publish - that just has NO effect on the actual documents.
I was hoping there would be a Sulu cookbook-recipe or another piece of documentation that extensively describes how to create fully published pages dynamically, thus without going through the 'manual labor' of the actual CMS environment, but so far I haven't found one... All help is much appreciated :)
PS: For the purposes of being complete: we're running Sulu on a Windows server environment on PHP 7.1; dbase is PostgreSQL, Sulu being a local forked version of release tag 1.4.7 because I had to make some changes to the way Sulu handles uploaded files to get it to work on a Windows environment.
EDIT: a partial solution for making a new house page if none exists already (not explicitly using the AdminKernel, but should of course be run in a context where the AdminKernel is active):
public function getOrCreateHuisPagina(Huis $huis)
{
$parent = $this->documentManager->find('/cmf/immo/routes/nl/huizen', 'nl'); // This is indeed the route document for the "collector page" of all the houses, but this doesn't seem to give any problems (see below)
try {
$document = $this->documentManager->find('/cmf/immo/routes/nl/huizen/' . $huis->id(), 'nl'); // Here I'm checking whether the page already exists
} catch(DocumentNotFoundException $e) {
$document = $this->setupPublishedPage();
$document->setTitle($huis->naam());
$document->setStructureType('huis_detail');
$document->setResourceSegment('/huizen');
$document->setParent($parent);
$document->getStructure()->bind([
'title' => $huis->naam(), // Not sure if this is required seeing as I already set the title
'huis_id' => $huis->id(),
]);
$this->documentManager->persist(
$document,
'nl',
[
'parent_path' => '/cmf/immo/contents/huizen', // Explicit path to the content document of the parnt
]
);
}
$this->documentManager->publish($document, 'nl');
return $document;
}
First of all I think the following line does not load what you want it to load:
$parentHousesDocument = $documentManager->find('/cmf/immo/routes/nl/huizen', 'nl');
It loads the route instead of the page document, so it should look like the following:
$parentHousesDocument = $documentManager->find('/cmf/immo/contents/nl/huizen', 'nl');
Regarding your error with the URL, instead of overriding the StructureSubscriber you should simple use the setResourceSegment method of the document, which does exactly what you need :-)
And the default_live workspace is wrong, is it possible that you are running these commands on the website kernel? The thing is that the WebsiteKernel has the default_live workspace as default, and therefore writes the content in this workspace. If you run the command with the AdminKernel it should land in the default workspace, and you should be able to copy it into the default_live workspace with the publish method of the DocumentManager.
I hope that helps :-)

Plone/Workflow- Is it possible to set the state of a workflow without needing a transition?

I have a content type (batch) that is tied to multiple instances of a different content (my_item) by an id. The my_item content type has a workflow consisting of draft, pending, and approved. There is a form that creates the batch and "approves" the my_item content type instances, and sets the my_item_instances' batch_id to the batch's batch id (set when the batch is created). The approved state is a final state, where it cannot be edited nor retracted.
I need to be able to change the state of the my_item content type instances back to draft. Since there is no transition for back to draft from the approved state when the item is being deleted (through a subscriber), I need to somehow set the state of the my_items back to "draft" without needing a transition.
There are two methods I tried:
The subscriber is IObjectWillBeRemoved
def my_item_will_be_removed(obj,event)
my_items = catalog.searchResults('batch_id':obj.batch_id)
for i in my_items:
api.content.transition(obj=i,to_state='pending')
This results in an error
InvalidParaemterError: Could not find workflow to set state to draft on
I also tried using:
wf_tool = api.portal.get_tool(name='portal_workflow')
wf_tool.setStatusOf('item_workflow',i,'pending')
For some reason that ends up with the my_item becoming a string.
Is it not possible? If it is possible, how can I do so?
Offtopic, but I guess a workaround I could use for now is:
make a transition "retract_from_approval" that goes from the approved state to the draft state
'can_retract_from_approval' needs to be assigned to the role that can delete the "batch"
In the deletion event, iterate through the my_items, assign the 'can_retract_from_approval' permission to the role responsible for deleting the batch locally on the current iteration
my_items = catalog.searchResults('batch_id',obj.batch_id)
for m in my_items:
mi_obj = m.getObject()
mi_obj.manage_permission('retract_from_approval',['ARole'],obj=mi_obj)
Then use the workflow tool to do the 'retract_from_approval' transition that sends the my_item back into the draft state. And then remove the 'can_retract_from_approval' permission.
This is a snippet of an old migration tool I used for migrate from Plone 2.5 to Plone 3 a lot of years ago.
wtool = getToolByName(obj, 'portal_workflow')
status = {'action': '',
'review_state': old_state,
'actor': 'admin',
'comments': 'Recovery state',
'time': DateTime() }
wtool.setStatusOf(workflow_id, obj, status)
not sure if it still works nowadays
you probably need a reindexObjectSecurity

un issues of MVC + Entity Framework

I am doing a project with ASP.NET MVC + Entity Framework, it's just like Google doc which can add some types of questions (for example, single choice question, multiple choice question etc. ),this is the Admin's part.
for user part, i dont kown how to save the results which have been chosen by users? for e.g. there are 3 questions: question 1, user chose "A", question 2 , user didnt chose, question 3 ,user chose "AB", and how can i get the results of user? should I use:
[HttpPost]
public ActionResult Index (FormCollection formCollection)
{
foreach (var res in formCollection.AllKeys)
something like this? but formCollection can just get the question which have been answered, so its value is [0]=A, [1]=AB,i wanna get [0]=A, [1]=null, [2]=AB, any ideas?
It's hard to tell without the code of the view, but I think you should set a default value on your questions options. Depending on how you present them, the default option could be hidden. So when you get an unanswered questions data, you would get [1]=-1 for example.

drools 5: NullPointerException when sharing KnowledgeBase across processes

We have a cluster of jboss instances running drools. We generate the KnowledgeBase on one instance, then store it in a database; the other instances can then load it from the database instead of generating it themselves.
This has worked well for some time using drools 4, but we recently upgraded to drools 5.3.0 BRMS and the pattern no longer works. The KnowledgeBase works fine within the app instance in which it was generated, but it fails everywhere else with the following exception. Note that this exception happens upon inserting a fact into working memory.
All instances of the app are identical, they run in the same jvm, etc. The 'other' instance does not need to be on a different physical machine -- it can be on the same hardware, and the failure still occurs.
What is it about a KnowledgeBase that makes it fail in a different jvm instance from the one in which it was generated? Is there some way to avoid this problem? Thanks.
This is the drl in which the failure seems to happen. I've done some debugging through the mvel code, but I'm not familiar with it and I haven't figured out what's going on. I think the expression on which the failure occurs is the 'this.fact.requirementId'.
rule "Find OutOfOrder PredicateSolutions for new LeafPredicateSolution in rule [Become Application Expired : 147]"
ruleflow-group "RuleGroup[Become Application Expired:147]"
agenda-group "leaf"
no-loop
salience -1001
when
EngineDates($rewindToDate: engineEffectiveDate);
lps:LeafPredicateSolution(
ruleId==147,
parent==null,
$candidateId:candidateId,
defunct==false,
fact:fact,
effectiveDate == $rewindToDate,
$programId:programId,
eval(!fact.getReused())); // only solutions created for the fact we're running on can trigger OoO
oops : ArrayList() from accumulate(
PredicateSolution(
$sol:this,
candidateId==$candidateId,
effectiveDate > $rewindToDate,
defunct==false,
programId==$programId,
parent!=null,
solutionType!=SolutionType.RETIRED,
eval(($sol instanceof LeafPredicateSolution) || ($sol instanceof NonCalcProgramSolutionChild))
),
init(ArrayList items = new ArrayList();),
action(items.add($sol);),
reverse(items.remove($sol);),
result(items));
// Find ProgramPredicateSolutions in other programs that refer to this program.
// These need to be rewound.
oopps:ArrayList() from collect(
ProgramPredicateSolution(
this.fact.requirementId == $programId,
candidateId==$candidateId,
effectiveDate > $rewindToDate,
defunct==false,
parent!=null,
solutionType!=SolutionType.RETIRED));
eval((oops.size() > 0) || (oopps.size() > 0));
eval(predicateSolutionFactory.oopEnabled());//Don't run oop if it is disabled (TimeMachine, Whatif)
then
predicateSolutionFactory.addToOutOfOrder(lps, oops, oopps, new Long(65));
end
Stack trace:
java.lang.NullPointerException
at org.mvel2.MVEL.executeExpression(MVEL.java:954)
at org.drools.base.extractors.MVELClassFieldReader.getValue(MVELClassFieldReader.java:100)
at org.drools.base.extractors.BaseObjectClassFieldReader.getHashCode(BaseObjectClassFieldReader.java:196)
at org.drools.core.util.AbstractHashTable$DoubleCompositeIndex.hashCodeOf(AbstractHashTable.java:616)
at org.drools.core.util.RightTupleIndexHashTable.getOrCreate(RightTupleIndexHashTable.java:451)
at org.drools.core.util.RightTupleIndexHashTable.add(RightTupleIndexHashTable.java:332)
at org.drools.reteoo.AccumulateNode.assertObject(AccumulateNode.java:263)
at org.drools.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:458)
at org.drools.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:386)
at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:135)
at org.drools.reteoo.SingleObjectSinkAdapter.propagateAssertObject(SingleObjectSinkAdapter.java:59)
at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:135)
at org.drools.reteoo.SingleObjectSinkAdapter.propagateAssertObject(SingleObjectSinkAdapter.java:59)
at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:135)
at org.drools.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:458)
at org.drools.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:376)
at org.drools.reteoo.ObjectTypeNode.assertObject(ObjectTypeNode.java:214)
at org.drools.reteoo.EntryPointNode.assertObject(EntryPointNode.java:244)
at org.drools.common.NamedEntryPoint.insert(NamedEntryPoint.java:337)
at org.drools.common.NamedEntryPoint.insert(NamedEntryPoint.java:298)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:887)
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:846)
at org.drools.impl.StatefulKnowledgeSessionImpl.insert(StatefulKnowledgeSessionImpl.java:267)