Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/metafeeds: Support for canteen meta feeds and index feed #2

Merged
merged 19 commits into from
Oct 22, 2018

Conversation

kaifabian
Copy link
Collaborator

This PR adds support for meta feeds (as documented in doc.openmensa.org Feed v2.1 and for a feed/canteen index (undocumented on doc.openmensa.org as of yet).

This PR creates incompatible URLs to provide a consistent naming scheme (/canteens/ for the canteen index, /canteens/<canteen>/meta for canteen metadata and /canteens/<canteen>/menu for the menu).

For both the index feed and metadata feed, a "reverse url" module (stw_potsdam/reverse.py) had to be added, that either uses a base URL derived from an environment variable (BASE_URL) or else from Flask's Request context.

Copy link
Owner

@f4lco f4lco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thorough work 👍 Change in URL structure and split into two feeds are very welcome. Will require a bit of Flask-ification :)

builder.address = canteen.street
builder.city = canteen.city

if skip_meta is not True:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: if not skip_meta: ...? Anyway, why parameterise? I'd prefer speaking names such as _create_meta_builder or _create_menu_builder. Latter could possibly be inlined, because it's just the constructor call for now.


for day in _active_days(menu):
_process_day(builder, day)

return builder.toXMLFeed()


def render_meta(canteen, menu):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

render_meta currently does not make use of (and should not require) a "menu" parameter.

(key, reverse("canteens/{}/meta".format(key))) for key in config
)

index_json = json.dumps(index)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the world of Flask, this is probably

from flask import jsonify
return jsonify(index)

Which adds convenience, configurability, and sensitivity to production / debug mode.
Second, I believe the method should belong to views. It is not related to feed building according to (Py)OpenMensa. It's possibly as simple as this two-line method:

# views.py
@app.route('/')
@app.route('/canteens')
def canteen_index():
    config = read_canteen_config()
    return jsonify({key: url_for('canteen_meta_feed', canteen_name=key, _external=True) for key in config})

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's right.
I additionally opened mswart/pyopenmensa#16, to discuss whether this should be a feature included in pyopenmensa or not.

from flask import request


def reverse(path):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were looking for flask.url_for, weren't you?

I suggest to keep knowledge about the existence of Flask views (and their names, parameters, etc) fully to the views module. feed.render_meta should receive the canteen and the menu URL (or possibly a function to retrieve menu URLs). Draft:

# views.py
from flask import url_for
def canteen_meta_feed_xml(canteen):
  menu_url = url_for('canteen_menu_feed', canteen_name=canteen.key, _external=True)
  xml = feed.render_meta(canteen, menu_url)
  return _canteen_feed_xml(xml)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, I was looking for flask.url_for, at least partially. I just was not sure how to 'override' this URL generation, which I want/need to use within my infrastructure, where the services are hidden behind a front-end proxy. (At least, I ran into that problem with my current implementation, before adding the BASE_URL environment variable). I will look into it. :)
At least, the urlparse.urljoin(request.url_root) foo can be replaced by a much more flask-esque flask.url_for.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I'm unaware of your exact requirements but here is a proposal: use config variable SERVER_NAME with the following patch:

# views.py
+import os
app = Flask(__name__)
+app.config['SERVER_NAME'] = os.environ.get('BASE_URL')

and use BASE_URL="spam.eggs:9090" make run to execute (or similar). url_for will then be aware of host + port when constructing absolute URLs. To my belief this should somewhat closely resemble what you have been attempting.



def canteen_meta_feed_xml(canteen, menu):
xml = feed.render_meta(canteen, menu)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"menu" parameter is obsolete. Also propagates into view and test methods.

stw_potsdam/views.py Show resolved Hide resolved
assert expected == actual


def test_index_consistency():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's moved inside the views module, this testcase is harder to implement. I think it's not required - it is testing more of the functionality of reverse or then Flask's url_for than anything else, I believe.

@kaifabian
Copy link
Collaborator Author

Thanks for your comments! :)
I will look into them and update my PR accordingly (probably on wednesday).

@kaifabian
Copy link
Collaborator Author

Better? ;)

return builder.toXMLFeed()


def render_meta(canteen):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your current approach looks very neat, I'm that close to approving. Main culprit: tests do not pass due errors in argument passing. Second, some things appear half-finished to me in feed.py:

  • Remove unused import statement for json. (Yes, at some time I should really think about integrating basic automatic static analysis / linting)
  • render_menu no longer needs a canteen parameter.
  • render_meta still references reverse function from ex-module reverse, which rightfully ceased to exist. It should take the canteen menu URL by parameter, populated via url_for inside the viewsmodule.

I estimate a required fix time of two minutes 😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, I see that I should not produce code when I'm in a hurry. I thought that all the tests passed (and I was surprised that everything worked out in the beginning), but it turns out that my test runner used the cached Docker image and not the current source code. 🤦‍♂️
Fixes should be up any minute.

@kaifabian
Copy link
Collaborator Author

Done so far. I just ran pycodestyle (former pep8) and pylint:
pep8 only complained about too long lines:

stw_potsdam\canteen.py:5:80: E501 line too long (81 > 79 characters)
stw_potsdam\canteen_api.py:9:80: E501 line too long (88 > 79 characters)
stw_potsdam\feed.py:36:80: E501 line too long (84 > 79 characters)
stw_potsdam\feed.py:37:80: E501 line too long (100 > 79 characters)
stw_potsdam\feed.py:68:80: E501 line too long (147 > 79 characters)
stw_potsdam\views.py:33:80: E501 line too long (99 > 79 characters)
stw_potsdam\views.py:34:80: E501 line too long (100 > 79 characters)
stw_potsdam\views.py:65:80: E501 line too long (90 > 79 characters)
stw_potsdam\views.py:98:80: E501 line too long (107 > 79 characters)
tests\test_retrieval.py:23:80: E501 line too long (99 > 79 characters)
tests\test_retrieval.py:29:80: E501 line too long (80 > 79 characters)

pylint (with disabled warnings missing-docstring and line-too-long) gives this:

************* Module stw_potsdam.canteen_api
C: 12, 0: Argument name "it" doesn't conform to snake_case naming style (invalid-name)
C:  5, 0: standard import "from collections import namedtuple" should be placed before "import requests" (wrong-import-order)
************* Module stw_potsdam.config
W:  7, 0: Relative import 'canteen', should be 'stw_potsdam.canteen' (relative-import)
C: 13,44: Variable name "f" doesn't conform to snake_case naming style (invalid-name)
************* Module stw_potsdam.feed
R: 36,12: Consider merging these isinstance calls to isinstance(price, (str, unicode)) (consider-merging-isinstance)
************* Module stw_potsdam.views
W:  9, 0: Relative import 'feed', should be 'stw_potsdam.feed' (relative-import)
W: 10, 0: Relative import 'config', should be 'stw_potsdam.config' (relative-import)
W: 11, 0: Relative import 'canteen_api', should be 'stw_potsdam.canteen_api' (relative-import)
C: 15, 0: Constant name "app" doesn't conform to UPPER_CASE naming style (invalid-name)
C: 19, 4: Constant name "base_url" doesn't conform to UPPER_CASE naming style (invalid-name)
C: 27, 0: Constant name "cache" doesn't conform to UPPER_CASE naming style (invalid-name)
E: 31, 4: Method 'logger' has no 'warn' member (no-member)
E: 42, 8: Method 'logger' has no 'info' member (no-member)
E: 47, 4: Method 'logger' has no 'info' member (no-member)
************* Module stw_potsdam.__main__
W:  2, 0: Relative import 'views', should be 'stw_potsdam.views' (relative-import)

Since only the base_url uppercase naming is related to this PR (and this is chosen to be consistent with the surrounding code), I think these should be addressed in another PR.
(The no-member errors are obviously false positives.)

If you'd like, I could also give those a try, but I would need to know which one you would want to be addressed, and probably you're quicker just fixing them directly. 😉

@f4lco
Copy link
Owner

f4lco commented Oct 22, 2018

Thanks for the info, yes, I'll postpone complaints of both tools. Those will be subject to later PRs. Are you generally interested in being a reviewer? I also noticed irregularities in the canteen API which require fixing (work in progress on my side).

@f4lco f4lco merged commit c5aacfe into f4lco:master Oct 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants