Jump to content

Recommended Posts

Hello everyone!

 

I just made a exchange rate workflow, check out on github and in packal!

 

Github -> https://github.com/kennedyoliveira/alfred-rates

 

Packal -> http://www.packal.org/workflow/rates

 

Its simple, but very useful.

 

I hope it can helps someone as help me.

 

Any feedback is appreciated!

 

Convert between many currencies using the YQL (Yahoo! Query Language) to get the exchange rates in realtime for free.

 

Screenshots

 

Convert from USD to EUR

1.png?raw=true

 

Convert from default configured currency (BRL in my case) to USD

2.png?raw=true

 

Autocomplete example

3.png?raw=true

 

Usage:

 

rate <VAL> <CUR SRC> <CUR DST> -> Convert the value from the currency to the currency . Example: rate 100 BRL USD

rate <VAL> <CUR DST> -> Convert the value to the default currency setted with ratesetcurrency comand.

rate <CUR SRC> <VAL> -> Convert the value from the currency to the default currency setted with ratesetcurrency command.

rate <CUR DST> -> Convert the default currency to the <CUR DST>, just to show the rates.

ratesetcurrency <CUR DST> -> Set the default currency, for use with the comands rate <VAL> <CUR DST> and rate <CUR SRC> <VAL>

rateclear -> Clears all the cached data, used when there is a new version for removing olds caches.

 

New Version 2.4.0!

 

Added support for multiple queries with one command.

 

5.png?raw=true

 

Added support for checking a conversion for many currencies, see below:

 

4.png?raw=true

 

Now the currencies list and info is online, so when there are new currencies update there's no need to update the workflow to get just the currency list, it'll be downloaded.

 

 

New Update :

  • Added support for basic math
    • Division
    • Multiplication
    • Sum
    • Substraction
  • Support for 4 digits after the decimal
  • Configurable division digit "," or "."
Edited by KennedyOliveira
Link to comment
  • 1 month later...
  • 2 weeks later...
  • 4 weeks later...
  • 1 month later...
  • 11 months later...

Handy workflow. Just a couple of questions/observations about your use of Alfred-Workflow.
 
Is it necessary to include requests? workflow.web should cover the needs of the workflow, and it's specifically written to be a standalone module that will work outside the context of an Alfred workflow (unlike the rest of the library, which is liable to explode without an info.plist), or is there some bug/lacking feature that requires requests?
 
There's no need to pass the Workflow object wf around between your functions (e.g. handle_check_update(), handle_get_default_divisor() etc.). Because it's created in the if __name__ == '__main__' block, it's global to the script.

You're not using Workflow.run() correctly. Workflow.run() doesn't care about the return value of the function you pass it (main()); it only responds to exceptions. So the line retorno = wf.run(main) will never receive the return value of main() and retorno will always be 0 unless an exception was raised by main(), in which case it will be 1.
 
If an exception does occur, Alfred-Workflow will call send_feedback() itself with an error message for the user. Your subsequent call to wf.send_feedback() (line 658 of rates.py) will either break the XML/JSON (meaning the user will only see Alfred's fallback searches) or be ignored by Alfred.
 
Instead of collecting return values in returns, you should do something like this in main():

err_count = 0
for query in queries:
    ...
    ...
    # no need to pass `wf`!
    err_msg = process_conversion(queries, query, currency_src, currency_dst, val, currencies)
    if err_msg:
        log.error(err_msg)
        err_count += 1
    ...
    ...

# Raise an exception if something went wrong
if err_count:
    # wf.run() will catch this exception, show the message in Alfred, then return 1
    # The logged error messages are also visible in Alfred's debugger, but it has to be
    # open during the workflow run.
    raise Exception('{} errors. Enter "rate workflow:openlog" for details.'.format(err_count))

# Won't be called if there was an error, so the XML/JSON output won't be broken
wf.send_feedback()

And then use sys.exit(wf.run(main)) in the if __name__ == '__main__' clause.

Edited by deanishe
Fix code example
Link to comment

Handy workflow. Just a couple of questions/observations about your use of Alfred-Workflow.

 

Is it necessary to include requests? workflow.web should cover the needs of the workflow, and it's specifically written to be a standalone module that will work outside the context of an Alfred workflow (unlike the rest of the library, which is liable to explode without an info.plist), or is there some bug/lacking feature that requires requests?

 

There's no need to pass the Workflow object wf around between your functions (e.g. handle_check_update(), handle_get_default_divisor() etc.). Because it's created in the if __name__ == '__main__' block, it's global to the script.

You're not using Workflow.run() correctly. Workflow.run() doesn't care about the return value of the function you pass it (main()); it only responds to exceptions. So the line retorno = wf.run(main) will never receive the return value of main() and retorno will always be 0 unless an exception was raised by main(), in which case it will be 1.

 

If an exception does occur, Alfred-Workflow will call send_feedback() itself with an error message for the user. Your subsequent call to wf.send_feedback() (line 658 of rates.py) will either break the XML/JSON (meaning the user will only see Alfred's fallback searches) or be ignored by Alfred.

 

Instead of collecting return values in returns, you should do something like this in main():

err_count = 0
for query in queries:
    ...
    ...
    # no need to pass `wf`!
    err_msg = process_conversion(queries, query, currency_src, currency_dst, val, currencies)
    if err_msg:
        log.error(err_msg)
        err_count += 1
    ...
    ...

# Raise an exception if something went wrong
if err_count:
    # wf.run() will catch this exception, show the message in Alfred, then return 1
    # The logged error messages are also visible in Alfred's debugger, but it has to be
    # open during the workflow run.
    raise Exception('{} errors. Enter "rate workflow:openlog" for details.'.format(err_count))

# Won't be called if there was an error, so the XML/JSON output won't be broken
wf.send_feedback()

And then use sys.exit(wf.run(main)) in the if __name__ == '__main__' clause.

 

Hello @deanishe!

 

Thank you for taking time to look at the code and give some advices, i wrote this workflow some time ago, i don't remember exactly why i choose somethings, but regarding your observations, i guess it doesn't need request, i probably forgot Alfred-Workflow had a web module, and implemented the feature with requests, i'll refactor and remove this in next version, if i find something missing which i doubt because is a simple GET for a file, but anyway, i 'll point to you.

 

About passing the wf reference around, i didn't remember this behavior, i probably didn't read the documentation well enough (shame on me haha), or i just don't remember right now or why the hell i did that, anyway, i'll refactor the code and read the documentation again so i don't miss any point.

Link to comment

About passing the wf reference around, i didn't remember this behavior, i probably didn't read the documentation well enough (shame on me haha), or i just don't remember right now or why the hell i did that, anyway, i'll refactor the code and read the documentation again so i don't miss any point.

That's not documented, tbh. At most it's just implied by the code examples.

Glad to hear it isn't a bug in workflow.web.

The return code handling isn't documented either (I had to go look at the Workflow.run() code to check). It honestly never occurred me to consider the return values of the called functions. I suppose zero/non-zero is a kind of de-facto standard, but I only focussed on unhandled exceptions rather than explicit error conditions like yours.

Edited by deanishe
Link to comment
  • 2 weeks later...

@deanishe, i started to refactor the code, but i remembered why i was passing the workflow around, due to unittesting the code, since the wf reference is created in __name__ == '__main__' block, and in the unittests i run the code directly, calling the main or the functions i'm testing, global workflow is not available, how you suggest testing this way?

 

I'm not a expert in python as you can see, hope you can help me. Thank you!

Link to comment

in the unittests i run the code directly, calling the main or the functions i'm testing, global workflow is not available, how you suggest testing this way?

 

If I were writing unit tests for a workflow, I'd initially try to write as many functions as possible so that they don't need the Workflow object (hence why my examples use a global log object, not Workflow.logger directly: if that's all you're using, it's easy to replace with a mock object, such as a real Logger with a NullHandler).

 

When you need a Workflow object, you could put wf = Workflow() at the top of each test function, so it'll be in scope when you call the functions you're testing.

 

But if you really want a global, Python gives you a reasonable workaround with the globals() function, which returns a (mutable) dictionary of all global variables.

As such, you could put globals()['wf'] = Workflow() in your setUp() and (optionally) del globals()['wf'] in your tearDown() (if you're using the standard unittest library).

 

As long as there's an info.plist beside or above your code, you should be able to instantiate a Workflow object without errors.

Does that help?

Edited by deanishe
Remove irrelevant waffle
Link to comment
  • 1 month later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...