Trying to start java scripting

Started by Carlo Didier, November 16, 2017, 11:33:06 AM

Previous topic - Next topic

Carlo Didier

After digging the oldest posts in https://www.photools.com/community/index.php?topic=6632.0, I tried a very very simple script, but I can't get it to work.
Can anyone push me in the right direction?

The goal is right now to list all categories (not in a web page!).

At the end, I want to read all categories (from a certain subtree, but that's for later) and get all images from the "Recently added" and "Recently updated" collections. Then I have to compare the image names with the descriptions in the categories and assign images to matching categories. I configured this in IM5 to avoid using RegExes in categoriy formulas because those slowed down IM quite a lot.

(using Notepad++ and Firefox right now. I don't want to install Chrome just for this if I can avoid it; my main browser is Edge anyway, Firefox is just there for the rare occasions that something doesn't work in Edge)

Mario

Your browser does not find the JavaScript files you are including. Hence IMWS is not defined. The console output is clear.

Assuming your script is in the webroot folder ?, remove the leading / in your script includes.

Tip: Notepad++ is old and not really good for working with JavaScript. I recommend Microsoft Visual Studio Code, plus the plug-ins we mentioned in the corresponding threads. Much better feedback, syntax checks etc: https://www.photools.com/community/index.php?topic=7111.0
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Removing the first "/" didn't help:
Loading failed for the <script> with source "file:///C:/ProgramData/photools.com/imatch6/webroot/user/Test/system/jquery/dist/jquery.min.js".
which looks logical ...

Why does the relative path work in the sample apps?

I tried various things and this at least runs:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">

        <!-- Force latest version of IE -->
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />

        <title>Sample App: Categories</title>
       
        <script src="file:///C:/ProgramData/photools.com/imatch6/webroot/system/jquery/dist/jquery.min.js"></script>
        <script src="file:///C:/ProgramData/photools.com/imatch6/webroot/system/imws/imwslib.js"></script>
        <script src="file:///C:/ProgramData/photools.com/imatch6/webroot/system/imatch/imatchlib.js"></script>


    </head>

    <body>

        <script>

IMWS.get('v1/categories',{
id: 'all',
fields:'id'
}).then(function(response) {
console.log("Result:");
arrCategories = response.categories
console.log(arrCategories);
},
function(error){
console.log("Didn't work ...");
console.log(error);
});

        </script>

    </body>
</html>


But it gives me an error I can't interprete:
Didn't work ...  index.html:31:5
Object { readyState: 0, getResponseHeader: getResponseHeader(), getAllResponseHeaders: getAllResponseHeaders(), setRequestHeader: setRequestHeader(), overrideMimeType: overrideMimeType(), statusCode: statusCode(), abort: abort(), state: state(), always: always(), catch: catch(), 8 more... }  index.html:32:5
Use of getPreventDefault() is deprecated.  Use defaultPrevented instead.


   

thrinn

Is it possible that you tried to open your index.html in an external browser using "File -> Open"?. That does not work. Your app has to be "served" by a web server (IMatch, in this case). Best to try it first in an App window.

Thorsten
Win 10 / 64, IMatch 2018, IMA

Carlo Didier

Quote from: thrinn on November 16, 2017, 12:23:07 PM
Is it possible that you tried to open your index.html in an external browser using "File -> Open"?. That does not work. Your app has to be "served" by a web server (IMatch, in this case). Best to try it first in an App window.

Eh, yes, I opened it in FF, as described in the threat I mentioned at the beginning ... and iMatch is running.
I don't know how to try it in an app window as it only outputs to the console (I don't need any user interface in the finished script).

thrinn

#5
"Opening an app in the browser" does not mean to open it via File -> Open. Instead, use something like the following URL:

http://127.0.0.1:50519/user/test-app/index.html

- 127.0.0.1 represents your local PC where IMatch is running. You could also write localhost instead.
- 50519 is the port number, configured in IMatch under Preferences -> Application -> Embedded Web Service
- The following part assumes that you use a standard directory layout for your App (I would recommend this for a start). More precisely:
All content that IMatch should serve lies under the webroot directory. On my PC, this is  C:\ProgramData\photools.com\imatch6\webroot. This includes apps Mario ships with IMatch (to be found in the imatch subdirectory), some system libraries like jquery (also shipped with IMatch), and last but not least a user sub directory where you should put your own apps.
For each of your apps, you should create a separate sub directory under webroot/user.

In my example, the sub dir is called test-app. It will be created by the App Wizard if you use it to create a skeleton for your app. And index.html is the main entry point to your app.

Just as background information: If you call your app (index.html) via an URL like above, it will not see your local file system. The Web Server is responsible for interpreting absolute and relative pathes in URLs. And the root of the "file system" starts at the webroot directory. Therefore, /system/... will in fact resolve to C:\ProgramData\photools.com\imatch6\webroot\system\.... But the local browser does not need to know this. The Web Server is doing the resolving.

Hope I can shed some light on your problems. We all struggled in the beginning to understand this nice new world...  :D
Thorsten
Win 10 / 64, IMatch 2018, IMA

Carlo Didier

Quote from: thrinn on November 16, 2017, 01:11:48 PM
All content that IMatch should serve lies under the webroot directory. On my PC, this is  C:\ProgramData\photools.com\imatch6\webroot. This includes apps Mario ships with IMatch (to be found in the imatch subdirectory), some system libraries like jquery (also shipped with IMatch), and last but not least a user sub directory where you should put your own apps.
For each of your apps, you should create a separate sub directory under webroot/user.

I had that part right.

And using "http://localhost:50519/user/Test/index.html" with the relative paths seems to work too!

Thanks a lot!

Carlo Didier

Dammit!

IMWS.get('v1/categories'...
worked (at least it returned an array of objects), but

IMWS.get('v1/categories?{auth}&path=@All|WHY'...
doesn't work, though it's exactly like the examples in the doc ...

Mario

Docs say:

The special id 'all' can be used to request all categories in the database (must be used alone). The special id 'root' (must be used alone) can be used to retrieve all top-level categories (below @All). The special id '@All' (must be used alone) can be used to retrieve the @All category."

If you try to use that in a HTTP request in the browser address bar, note that @ has a special meaning and you need to escape it properly. If you use it via jQuery or an AJAX call, you can use it as is. For more info about this, refer to MDN.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 16, 2017, 03:54:00 PMIf you try to use that in a HTTP request in the browser address bar, note that @ has a special meaning and you need to escape it properly.
Stupid question: how?

Carlo Didier

From the iMatch webservices doc:
http://127.0.0.1:50519/v1/categories?{auth}&​path=@All|location|london
Retrieve data for the category with the given path.

no escaping there ...

Carlo Didier

#11
This just doesn't work ...  ???  :-\  :'(

       <script>

            // get the event categories
IMWS.get('v1/categories?{auth}&path=@All|WHY',{
                        id: 'all',
fields:'id'
}).then(function(response) {
console.log("Event categories:");
arrCategories = response.categories
console.log(arrCategories);

                               // get the new images
                               IMWS.get('v1/categories?{auth}&path=@All|QUEUES|_new',{
                               id: 'all',
                               fields:'id'
                               }).then(function(response) {
                                   console.log("New files:");
                                   arrNewFiles = response.files
                                   console.log(arrNewFiles);
               
                                   // do the magic here ...
                   
                               },
                               function(error){
                                   console.log("Getting files didn't work ...");
                                   console.log(error);
                               });
},
function(error){
console.log("Getting event categories didn't work ...");
console.log(error);
});

        </script>

thrinn

In your script, you don't build the URL string by hand. Just use IMWS.get('v1/categories', {... }) where the part between { ... } is the parameter object.
To get all categories you have, use id: 'all' . To get a category like @All|WO, use the path: 'WO' instead. And you can "chain" calls like this:


   <script>
        // To get all categories available in the database:
        IMWS.get('v1/categories',
            {
                id: 'all',
                fields: 'id'
            }).then(response => {
                // response now contains an array with all categories
                IMatch.consoleLog(JSON.stringify(response, null, 2));
                // You can "return" the response together with the result of the second call to v1/categories
                return Promise.all([response,
                    // To get the WO category (@All|WO) specify it as a path (without the @All prefix)
                    IMatch.get('v1/categories', {
                        path: 'WO',
                        fields: 'id',
                        recursive: true         // Want to get all children
                    })
                ]);
            }).then(respArray => {
                // respArray[0] is the response from the FIRST call (with id: 'all')
                // respArray[1] is the response from the SECOND call (with path: 'WO')
                IMatch.consoleLog(JSON.stringify(response, null, 2));

            }).catch(err => {
                IMatch.consoleLog(JSON.stringify(err, null, 2));
            })

    </script>


Thorsten
Win 10 / 64, IMatch 2018, IMA

Mario

I can only recommend to start with the beginning.
Look at the sample apps I ship with IMatch. Their source code is fully documented.

Tip: In Visual Studio Code, use the "Open Folder..." command and then open the folder

C:\ProgramData\photools.com\IMatch6\webroot

This brings all sample apps and IMatch apps into one workspace. Then you're ready to explorer.

Also, read the mandatory Code Recipes help page which explains things in great detail, with copy/paste code fragments.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Jingo

I can 2nd Mario's thoughts... when I was starting out - I took an entire weekend and played with one simple script... just getting and setting a keyword.  I expanded that code as I learned the nuances of JS and how it interacts with the IMWS...  I tried hard not to jump right into coding the apps I wanted - but took the old "Hello World" approach which has always served me well no matter the language... 

Carlo Didier

I did all that. I took pieces from the sample app about categories to start with. But no sample app does even remotely what I want. And all do just one little thing and always with user interaction, which I don't need or want.
I try to follow the documentation. It gives an example like "http://127.0.0.1:50519/v1/categories?{auth}&​path=@All" but that doesn't work and you tell me I have to escape the "@" if used in a browser, but that's not what the doc says. I'm lost ... All I get is "Error 400: Bad Request. Invalid URI".

So I just forget about testing single commands directly in the browser.

In my test script, I tried another syntax (which I didn't find in the examples from the doc but in the sample apps):
    IMWS.get('v1/categories',{
        path: '@All|WHY|EVENTS',
        recursive: true,
        id: 'all',
        fields:'id,name,description'

instead of
    IMWS.get('v1/categories?{auth}&path=@All|WHY|EVENTS&recursive=true',{
        id: 'all',
        fields:'id,name,description'

which works to a point. It returns an array of over 3800 objects, probably all the categories in my database instead of only ~150 under @All|WHY|EVENTS ...
The doc says the parameter "id" is optional, so I left it out, but then I an empty array. Optional? Pfft!

It can't be that difficult to get a list of categories!

Mario

Either use path or id or idlist.
IMWS looks first for id and since you supplied that, all cats are returned.
You don't specify @All in the path.

path: 'WHY|EVENTS'

will work.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Ok, small step forward:
IMWS.get('v1/categories',{
            path: 'WHY|EVENTS',
            recursive: true,
            fields:'id,name,description'
          }).then(function(response) { ...

gives me one object, which is the "EVENTS" category. But with the "recursive: true", shouldn't it give me all the categories under "EVENTS"?

Carlo Didier

Quote from: thrinn on November 16, 2017, 04:50:17 PM
In your script, you don't build the URL string by hand. Just use IMWS.get('v1/categories', {... }) where the part between { ... } is the parameter object.
To get all categories you have, use id: 'all' . To get a category like @All|WO, use the path: 'WO' instead. And you can "chain" calls like this:


   <script>
        // To get all categories available in the database:
        IMWS.get('v1/categories',
            {
                id: 'all',
                fields: 'id'
            }).then(response => {
                // response now contains an array with all categories
                IMatch.consoleLog(JSON.stringify(response, null, 2));
                // You can "return" the response together with the result of the second call to v1/categories
                return Promise.all([response,
                    // To get the WO category (@All|WO) specify it as a path (without the @All prefix)
                    IMatch.get('v1/categories', {
                        path: 'WO',
                        fields: 'id',
                        recursive: true         // Want to get all children
                    })
                ]);
            }).then(respArray => {
                // respArray[0] is the response from the FIRST call (with id: 'all')
                // respArray[1] is the response from the SECOND call (with path: 'WO')
                IMatch.consoleLog(JSON.stringify(response, null, 2));

            }).catch(err => {
                IMatch.consoleLog(JSON.stringify(err, null, 2));
            })

    </script>


Thanks for the response, Thorsten. But I don't understand what the "return Promise.all" part means. I don't want to return anything, just put the resulting array of category objects in a variable to use later in the script. And I tried the "recursive: true", but that doesn't seem to work (see my previous post).

Mario

Quote from: Carlo Didier on November 16, 2017, 08:31:09 PM
Ok, small step forward:
gives me one object, which is the "EVENTS" category. But with the "recursive: true", shouldn't it give me all the categories under "EVENTS"?

You did not ask IMWS to return the children so they are not included!
For performance and bandwidth reasons, IMWS only returns the requested fields. Or all fields, if no specific fields are requested.

If you use fields:'id,name,description,children'

IMWS will include the child categories, not only id, name and description.
IMWS then returns a properly formatted JavaScript object with an array for the children. And since you asked for recursive, if the children have children they will also have arrays for their child categories.



-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Ah, ok. The doc says "If this option is specified as true, child categories are returned recursively." which for me meant that the recursive parameter was sufficient to get all child categories. Maybe you could clarify that point in the doc.

Does that mean that the result is not a flat array of all categories of all levels, but a nested array of arrays of arrays (for each level of categories, one level of arrays)?
If that's the case, is there no way to get a flat array?

Carlo Didier

Ok, I see it's not flat ... makes it a lot more complicated than I thought.

Besides that, I tried to further filter the categories by description. From what I found in the sample app, I tried this, but it still returns all categories, regardless of the description.
IMWS.get('v1/categories',{
            path: 'WHY|EVENTS',
            regexp: 'desc,"D[0-9]{11}-D[0-9]{11}"',
            recursive: true,
            fields:'id,name,description,children'
}).then(function(response) {

I tried all variantions of " and ' to no avail. It's the same expression I use in my old script.

Mario

'desc,"D[0-9]{11}-D[0-9]{11}"',

Why do you use " around your expression"? Do you search for descriptions starting with " and ending with " ?
The docs say: path,beach.*
No " "

You can also just get all categories and pick them by whatever criteria. Delivering a few thousand categories takes IMWS less than one second.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Mario

Quote from: Carlo Didier on November 16, 2017, 09:28:45 PM
If that's the case, is there no way to get a flat array?
Why should there be. Categories are hierarchical.
You can use standard JavaScript methods to recurse down and flatten the categories as you go.
Or get all categories in a single array and then process the ones you need.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 16, 2017, 10:15:55 PM
You can use standard JavaScript methods to recurse down and flatten the categories as you go.
Or get all categories in a single array and then process the ones you need.
You know that sounds to me like
Quotedvf sdfsdfgbsdf ngsdlfgj htslgdfg  -dfkgl n.klxflgghjb sdufgé iusrgtibhsupégijéhgih sdrtf
ksdufg s.dgfklhj sdflgkj jfdxsgbjh élkdfghjsdfégi j-dfkgbhj fglbkdfjl
:)

In IM5, this is how I did it:
Dim objCategory As Category
Dim regExpression As New IMRegExp

' regex to find categories which have a description corresponding to a range for an event
regExpression.SetExpression("D[0-9]{11}-D[0-9]{11}",False)

For Each objCategory In Database.Categories

If regExpression.Match(objCategory.Description) = IMRegExpResult.RegExpMatch Then ' we have an event category
       ......

which got me all the categories with a description corresponding to my regex.
Very simple and straightforward. Exactly what I need. Do I have to write 100 lines of Java to get the same result those 5 lines in Basic gave me? Why should I have to "manually" walk through all the levels of the hierarchy?

Carlo Didier

Just an off-topic note:

I just had a little fight with one of our Exchange 2010 servers. It wouldn't install the latest rollup. Finally found out it was .NET 4.5 blocking. Had to uninstall .NET 4.5, install the rollup and then re-install .NET 4.5 because we need that for the Netap Snapmanager to do the backups ... I love IT  8)

But still, such problems seem simple to me compared to my Java problems  :)

Mario

#26
For Each objCategory In Database.Categories

What I said. You get all categories, iterate over them and do whatever you want.
Same you can do with JS when you query IMWS for all categories in a flat list. Why don't you?
Or fix the regexp and IMWS will do it for you.

If you find JavaScript too difficult, why don't you use PowerShell or Java or C# or VB.NET? It all works with IMatch 2017 and IMWS. Or use curl. Or PHP. Or Python. Or Go. Or Koitlin, ...

Same interface for all programming languages.
Works locally with IMatch. Or remotely with IMWS. Even when IMWS runs on a computer on the other side of the world.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 16, 2017, 11:09:26 PM
Same you can do with JS when you query IMWS for all categories in a flat list. Why don't you?
Or fix the regexp and IMWS will do it for you.
How? Like this?
        IMatch.get('v1/categories',{
            id: 'all',
            regexp: 'desc,D[0-9]{11}-D[0-9]{11}',
            fields:'id,name,description,children'

But I can't get the regex to work.
Neither does this:
        IMatch.get('v1/categories',{
            id: 'all',
            regexp: 'desc,"D[0-9]{11}-D[0-9]{11}"',
            fields:'id,name,description,children'

And I can't find out how to test a category description against a regex. The java syntax would be something like
if (strDesc.matches('D[0-9]{11}-D[0-9]{11}')) {
but that gives me an error
Quote... strDesc.matches is not a function ...


Mario

-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 16, 2017, 11:32:10 PM
What is strDesc ?
Just a variable containing the description from a category.

But my main question is: why does this not work:
IMatch.get('v1/categories',{
            id: 'all',
            regexp: 'desc,"D[0-9]{11}-D[0-9]{11}"',
            fields:'id,name,description,children'


Shouldn't it only return categories where the description corresponds with the regex?
Note that I still haven't found anything about the exact syntax for "regex: ..." in the above case. Is it correct? Are the "s and 's correctly placed?

Or is the regex only considered without the "id: 'all'"?
That would mean that to find all the categories matching a certain description I have to either walk through the whole hierarchy (lots of coding, complex, error prone) or just go with "id: 'all'" to get all the categories (slower and unnecessary) and then filter afterwards (if I get that test working).

Carlo Didier

Quote from: Mario on November 16, 2017, 11:32:10 PM
What is strDesc ?

https://www.w3schools.com/jsref/jsref_obj_regexp.asp
https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/RegExp

After a lot of trying and finally finding a realworld example in your second link, I got my test to work:
if (strDesc.match('D[0-9]{11}-D[0-9]{11}') != null) { ...

But I'd still prefer to filter right at the query where I get the category list.

Mario

Don't specify an id if you use regexp.
Don't wrap your regexp in "
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 17, 2017, 09:06:22 AM
Don't specify an id if you use regexp.
Don't wrap your regexp in "

That returns all the categories starting at the path I indicate, but the regex doesn't work. It returns all categories, regardless of the description. And it doesn't return a flat list, but a hierarchical one which is much more complex to handle ...
IMatch.get('v1/categories',{
            path: 'WHY|EVENTS',
            regexp: 'desc,D[0-9]{11}-D[0-9]{11}',
            recursive: true,
            fields:'id,name,description,children'


But I think I found it now. It looks like "path" and "regex" are mutually exclusive.
When I use only "path", I get all categories under that path in a hierarchical structure.
But when I only use "regex", I get all the categories in the database matching the description I'm looking for in the regex ... as a flat list! Not consistent!
When I use both, "regex" is ignored.

You might consider the fact that in one case you get a hierarchical list and in the other case a flat list as a bug. If so, I'll file a bug report for that.

The fact that the two criteria "path" and "regex" are mutually exclusive is not indicated in the doc. Both are just labeled as optional. Bug or just an oversight in the doc?

Mario

Use either id, path, idlist, regexp, regexpcs. path overrides regexp.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 17, 2017, 11:58:39 AM
Use either id, path, idlist, regexp, regexpcs. path overrides regexp.
Ok, then please update the doc, because that's not clear there.

Btw, looks like my first new script is finally working. Just a few cosmetic things and I'll share it here. Not that someone else could use it, it's way too specific for my workflow, but nonetheless it could be useful as a sample. And maybe someone will give me tips to optimize it.

Thanks to you Mario and Thorsten for your help and patience!

Mario

#35
QuoteOk, then please update the doc, because that's not clear there.


Thank you for improving the documentation.
I've already incorporated a sentence that makes it more clear that you can use only one of the selector parameters.

Note: In case you fall for the same issue with other endpoints: It's always one of the selectors. Either id or idlist or path. Or whatever other selectors are supported by a specific endpoint.

Please don''t expect that I've got everything right the first time. Or even anticipated and answered any potential questions.
Programming is a technical task and some suffering is implicit.
-- Mario
IMatch Developer
Forum Administrator
http://www.photools.com  -  Contact & Support - Follow me on 𝕏 - Like photools.com on Facebook

Carlo Didier

Quote from: Mario on November 17, 2017, 03:02:47 PM
Please don''t expect that I've got everything right the first time. Or even anticipated and answered any potential questions.

I'm pretty sure nobody expects that. But we all know that you are listening and continuously improving IMatch and the doc and we all want to help you with that.