Giter Club home page Giter Club logo

gourmet's Introduction

Gourmet Recipe Manager

Try it. It's good!

Tests Build

Introduction

Gourmet Recipe Manager is a manager, editor, and organizer for recipes.
It has a plugin architecture which allows you to enable extensions to Gourmet's base functionality.
For example, there is a nutritional plugin that allows Gourmet to help you calculate nutritional information for any recipe. There are also a wide variety of import and export plugins that let Gourmet read and write recipes in various formats.

Requirements and Installation

Installation instruction are found in INSTALL.md.

Issues and Contributions

See the contribution guide.

gourmet's People

Contributors

agsimmons avatar ahrendsen avatar cydanil avatar eginhard avatar fbobraga avatar friedrichfroebel avatar gnarlyquack avatar googlymog avatar holocronweaver avatar jraber avatar kelvinhammond avatar kirienko avatar kratz00 avatar lamecarlate avatar lyra2108 avatar maweki avatar miska avatar mrshark avatar nbeaver avatar ockham avatar ozarkshepherd avatar pando85 avatar pounamu avatar rebeccaloran avatar sappjw avatar saxon-s avatar takluyver avatar thinkle avatar vollkorn1982 avatar wummel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gourmet's Issues

Fix test_exporters.py

This one might be not an easy task.

This is a very old test (2006) which relies on some reference_setup.
It tries to import an argument that presumably no longer exist.
The architecture of this test is also unclear. Maybe it's easier to rewrite the whole test from scratch.

Priority is unclear (from medium to low).

setup doesn't check dependencies (review of dependencies)

I don't know whether the setup.py should check dependencies. But I am currently trying to install gourmet in a basically empty docker (actually podman) container. It's a toolbox container that should allow you to install dev dependencies and stuff without cluttering your (possibly immutable) installation. I am doing all of this on fedora.

First it failed with:

running build_i18n
intltool-update -p -g gourmet
unable to execute 'intltool-update': No such file or directory
error: command 'intltool-update' failed with exit status 1

I fixed that with sudo dnf install intltool

Then I got many error messages sh: file: command not found which I fixed with sudo dnf install file. Then it worked. This might be a documentation issue but to get all the stuff built/installed from the requirements.txt I had to install all the following:

sudo dnf install cairo-devel gcc python3-devel gobject-introspection-devel cairo-gobject-devel

Then trying to run gourmet I get the following error:

Traceback (most recent call last):
  File "/home/maweki/.local/bin/gourmet", line 481, in <module>
    from gourmet import GourmetRecipeManager
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/GourmetRecipeManager.py", line 12, in <module>
    from gi.repository import Gtk
  File "/home/maweki/.local/lib/python3.8/site-packages/gi/importer.py", line 132, in load_module
    raise ImportError('cannot import name %s, '
ImportError: cannot import name Gtk, introspection typelib not found

As it turns out, I still needed to install gtk3, which was a no-brainer sudo dnf install gtk3.

Then I got the following error:

Traceback (most recent call last):
  File "/home/maweki/.local/bin/gourmet", line 513, in <module>
    GourmetRecipeManager.startGUI()
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/GourmetRecipeManager.py", line 707, in startGUI
    r=RecGui(splash_label=splash.label)
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/GourmetRecipeManager.py", line 934, in __init__
    GourmetApplication.__init__(self, splash_label=splash_label)
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/GourmetRecipeManager.py", line 84, in __init__
    self.setup_prefs() # Setup preferences...
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/GourmetRecipeManager.py", line 102, in setup_prefs
    self.prefsGui = prefsGui.PreferencesGui(
  File "/home/maweki/.local/lib/python3.8/site-packages/gourmet/prefsGui.py", line 61, in __init__
    self.ui.add_from_file(uifile)
gi.repository.GLib.Error: g-file-error-quark: Failed to open file “/home/maweki/.local/share/ui/preferenceDialog.ui”: No such file or directory (4)

and this is actually #107

This was just a review of all dependencies actually needed to build gourmet from source with all the dependencies. I have not checked what is actually documented and what isn't.

Fix test_gui.py

This test uses dogtail testing package.

It is unclear if this approach still works, because it looks like the setup is broken.
But if it works, then we might use this approach for #23

Priority and importance: high

Create tests for language classes

There are some restrictions on the language classes where some keys in one section need to reappear in others. This can't be expressed by types. As the language classes are now easily importable, we can just check that as unit tests.

Refactor IngredientController.modules to be a dict

IngredientController.modules is a list that contains the different UIs ({Description,Ingredient,Instructions,Notes}EditorModules) in the attached to a recipe.

These are now kept in a list, IngredientController.modules, where they are accessed by index.
The correct index for a given module can be found in IngredientController.module_tab_by_name.

This tends to litter the code with a bunch of hardcoded indices, even though the correct way to get a controller is:

idx = i_controller.module_tab_by_name["ingredients"]
ingredients = i_controller.module[idx]

Historically, each EditorModule was kept straight in the class as class attributes. I'm not sure what motivated this change.

However, it would be nicer to simply refactor IngredientController.modules to be a dictionary, to access the modules by their names instead of indices:

ingredients = i_controller.module["ingredients"]

Main View Star Rating change isn't reflected in UI

If you are in the main view and have an item selected, you can select a new star rating, which is shown after you restart gourmet. The change is not reflected immediately.

On a maybe related issue: if the current view is sorted by star rating and you change the rating, the un-updated view resorts itself in a strange manner (it looks like duplicate entries).

Problematically, there is no error shown.

Fix test_db.py

One error, one failure — out of 10 tests:

FAILED gourmet/tests/test_db.py::testUnicode::runTest - AttributeError: 'str' object has no attribute 'decode'
FAILED gourmet/tests/test_db.py::TestMoreDataStuff::test_image_data - AssertionError: 'None' != 'ÿØÿà\x00\x10JFIF\x00\x01\x01\x00\x00\x01\[2147 chars]0fÿÙ'

AttributeError might be an easy fix.
AssertionError is probably image related.

Medium priority.

Consider removing custom styling in RecCardDisplay

I'd like to remove the custom styling in RecCardDisplay, implemented here in setup_style

This is the view for a recipe. The current Gtk2 code is outdated, and needs to be ported to Gtk3, and is major blocker for fixing the record card and its tests.

I've done it, and I find the result horrible:
with_custom_styling

Whereas the default theme, which depends on your desktop environment settings, looks so for me (xfce4 with the Linux Mint-Y theme):

default_theme

By removing it, we also remove questionable code.

(unrelated, the recipe is hot chocolate with a twist for cold evenings)

Detect theme brightness in usage

It's more and more common for users to have dark modes.

Most of Gourmet plays nice with dark modes, short of url markup, which is for now hardcoded as blue here:

class LinkedPangoBuffer (PangoBuffer):
href_regexp = re.compile(r"<a href=['\"]([^'\"]+)['\"][^>]*>(.*?)</a>")
url_markup = 'underline="single" color="blue"'
url_props = [('underline',Pango.Underline.SINGLE),
('foreground-gdk',Gdk.color_parse('blue')),
]

which looks painful in dark themes:

image

Dark theme detection can be handled using GtkSettings:

In [1]: from gi.repository import Gtk                                                                                                                                    

In [2]: settings = Gtk.Settings()                                                                                                                                        

In [3]: settings.get_property("gtk-application-prefer-dark-theme")                                                                                                       
Out[3]: False

image

There are only two files affected: timeScanner.py and gtk_extra/LinkedTextView.py.
I don't know how cross-platform this solution is. I think it is, since that uses the standard GTK environment.

GUI doesn't start

GUI cannot be started with existing recipes.db.

Traceback (most recent call last):
  File "bin/gourmet", line 17, in <module>
    GourmetRecipeManager.startGUI()
  File "./gourmet/GourmetRecipeManager.py", line 704, in startGUI
    r=RecGui(splash_label=splash.label)
  File "./gourmet/GourmetRecipeManager.py", line 924, in __init__
    editable=False)
  File "./gourmet/recindex.py", line 65, in __init__
    self.setup_widgets()
  File "./gourmet/recindex.py", line 106, in setup_widgets
    self.setup_rectree()
  File "./gourmet/recindex.py", line 222, in setup_rectree
    self.create_rmodel(self.rvw)
  File "./gourmet/recindex.py", line 217, in create_rmodel
    self.rmodel = RecipeModel(vw,self.rd,per_page=self.prefs.get('recipes_per_page',12))
  File "./gourmet/recindex.py", line 663, in __init__
    per_page=per_page)
  File "./gourmet/gtk_extras/pageable_store.py", line 323, in __init__
    per_page=per_page)
  File "./gourmet/gtk_extras/pageable_store.py", line 50, in __init__
    self.update_tree()
  File "./gourmet/gtk_extras/pageable_store.py", line 158, in update_tree
    self.append(row)
  File "/usr/lib/python3/dist-packages/gi/overrides/Gtk.py", line 971, in append
    return self._do_insert(-1, row)
  File "/usr/lib/python3/dist-packages/gi/overrides/Gtk.py", line 962, in _do_insert
    row, columns = self._convert_row(row)
  File "/usr/lib/python3/dist-packages/gi/overrides/Gtk.py", line 862, in _convert_row
    raise ValueError('row sequence has the incorrect number of elements')
ValueError: row sequence has the incorrect number of elements

dogtail only works with specific accessibility settings

According to the docs (and also via testing), dogtail needs certain accessibility settings to be run.

Currently GNOME and GTK+ applications are supported. Thanks to qt-at-spi KDE4 and QT applications are now available too.

First, enable accessibility support in your GNOME session with: gsettings set org.gnome.desktop.interface toolkit-accessibility true This only affects newly-started applications, so you may want to log out and log back in again.

If you are using KDE instead, install the 'qt-at-spi' QT plugin and make sure you QT_ACCESSIBILITY set to 1 throughout your environment (you can put 'export QT_ACCESSIBILITY=1' to your profile file). QT accessibility should be stable from QT 4.8.3 onward.

If this isn't the case, nosetests just fails silently. This also means, testing these parts is not supported on windows.

We could:

  • set/reset this stuff in the setUp and tearDown methods in those classes.
  • force it onto the users when they want to run tests.
  • create a test-runner bash-script

State of the recipe import parsers

With #33, I started working on the existing import parsers (and intended to fix the remaining ones as well). While doing this, I stumbled upon some stuff which I am not sure about:

  1. The tests rely on rather outdated local files, fixes to the code therefore will just satisfy the test cases, but not reflect the reality. As soon as someone tries to use the parser with the current site, it will break again and we have to re-write it. Should we therefore consider using the current site and parse the data from there? (See #20 as well.)

  2. In my opinion the parser code is a bit weird (and I do not really get with it is a preparse and not a real parse). In the old version, it would create a list of tuples like this:

    [(Tag, 'recipe'), (Tag, 'title'), (Tag, 'ingredients'), (Tag, 'ingredients')]
    

    where Tag is a HTML element tag returned by BeautifulSoup. To get the list of ingredients later, we would iterate over this list and check whether the second tuple element matches the string ingredients. For me this does not look very efficient as we could use a dictionary data['ingredients'] = ['ingredient1', 'ingredient2'] (or a simple data class) as well, where we might not even save the tags (I am not sure why they are saved in favor of their text, but I did not fully analyze the current code). With #33 I have switched to saving the texts instead of the tags, but this probably broke the real usage outside the tests.

    Do we want to change anything about it for now?

Main view not consistent after star changes

So I've added new recipes (empty) and sorted by star. When changing the star rating, the item swaps to is correct position but receives a different id and name. When double-clicking I get the wrong recipe (the one that fits the title, cranberry cookies in that case) and not the empty one. I guess there's a +-1 error somewhere.

output

Adding xvfb-run to test CI runner

I'm now working on fixing test_reccard.py, and it does loads of Gtk operations, one of which is to launch a window. This may not work nicely in the CI, where there's no X server.

I'm not there yet, but I will be facing this issue.

I suggest we follow the PyGObject docs on CI, and add xvfb-run to the CI.

(xvfb-run has been superseeded by xorg-xf86-video-dummy, but I don't know enough to make an informed suggestion.)

That's indeed adding a dependency, but would be required only within the test CI runner.

Convert.seconds_to_timestring only converts to seconds

Convert.seconds_to_timestring is a function used in few places: in the index, the recipe card (reccard), timeEntry, and a few export plugins.

Its use is to convert time, which is internally stored as ints representing seconds, to some nicer format such as 2 hours and 5 minutes.

screenshot of time rendered in seconds, instead of minutes

I'd say the priority is medium: it's not hard to fix, but the usability of the application is affected:

The current implementation could be fixed using something like this but internationalization might be tricky:

from datetime import timedelta

def seconds_to_timestring(seconds: int, 
                          round_at: Optional[int] = None, 
                          fractions: Enum[TIME_FRACTIONS] = FRACTIONS_NORMAL):

    mins, secs = divmod(seconds, 60)
    hrs, mins = divmod(mins, 60)
    days, hrs = divmod(hrs, 24)

   time_string = ""
   if days:
      d = "days" if days > 1 else "day"
       time_string += f"{days} {d} "  # note the extra whitespace

  if hrs:
     h = "hours" if hrs > 1 else "hour"
     time_string += f"{hrs} {h} "  # note the extra whitespace

   ...

I don't think we care so much about period of times greater than a year. I also think that the fractions are not such a big deal (3/4 hour instead of 45 mins?).

RecipeManager.parse_ingredient does not handle decimal values correctly

In the record card, it's possible to write a line for an ingredient, in a semi free format:

1.5 cup oil
1 c. oil
1.5 c. oil
4/5 tsb. oil

However, the parser for this content is broken, due to an incorrect regex, and returns something along the lines of: {'amount': '1', 'unit': '.5', 'ingredient': '.c oil'}

This is an old issue that has already been present in previous releases of Gourmet.

The regex is defined here, of which the faulty sub-regex is defined here.

Moreover, commas in decimal values (eg. 1,5) are not supported.

Fix test_exportManager.py

This test spawns a GUI window. (One can check it using pytest.) Therefore it cannot be used in the automated mode and needs to be rewritten.

Nonetheless, two of three tests are passing in this manual mode. Fixing the rest might be trivial:

TypeError: catching classes that do not inherit from BaseException is not allowed

Update INSTALL.md

The current version of INSTALL.md refers to the python2 version and therefore almost useless and misleading now. It needs to be updated.

recindex.py: sort() takes no positional arguments

Traceback (most recent call last):
  File "./gourmet/recindex.py", line 319, in <lambda>
    get_colnum(tc),
  File "./gourmet/convert.py", line 573, in seconds_to_timestring
    units.sort(lambda a,b: a[1]<b[1] and 1 or a[1]>b[1] and -1 or 0)
TypeError: sort() takes no positional arguments

This needs to be a new-style key function. If we don't want to write our own, we can use https://docs.python.org/3/library/functools.html#functools.cmp_to_key to create one from the given function. List.sort() takes the argument as keyword-arg key (https://docs.python.org/3/library/stdtypes.html#list.sort) but we might want to rewrite a bit of this section.

This is an easy fix and I am happy to do it tomorrow.

Missing dependencies

I'm listing here some dependencies that I see missing when executing tests.

So far, they all are dependencies for plugins.

  • poppler (discontinued?) required by plugins/import_export/pdf_plugin/print_plugin.py
  • gtkspellcheck required by plugins/spellcheck/reccard_spellcheck_plugin.py

Fix test_interactive_imported

The test looks relatively simple, but again there is an import-related problem somewhere.

TypeError: catching classes that do not inherit from BaseException is not allowed

Priority: medium

Rewrite PossiblyCaseInsensitiveDictionary

The PossiblyCaseInsensitiveDictionary is very python2. I am in favor of rewriting it in python3. I implemented a few different dictionary-like objects in my time and I am up for it.

Here is the original code:

class PossiblyCaseInsensitiveDictionary (dict):

    transformations = ["lower","title","upper"]

    def has_key (self, k):
        if dict.has_key(self,k): return True
        else:
            for t in self.transformations:
                if hasattr(k,t):
                    if dict.has_key(self,getattr(k,t)()): return True
        return False

    def __getitem__ (self, k):
        if dict.has_key(self,k):
            return dict.__getitem__(self,k)
        else:
            for t in self.transformations:
                if hasattr(k,t):
                    nk = getattr(k,t)()
                    if dict.has_key(self,nk):
                        return dict.__getitem__(self,nk)
        # Raise plain old error
        dict.__getitem__(self,k)

I will use abstract base class as parent class, the transformations will become lambdas, has_key will become a dunder-method.

Create tests for KeyManager

The KeyManager role is to reduce as much as possible the different spelling of categories.
Such categories are ingredients, food groups (regions of recipes).

Now that we understand a bit what it does and how it works, we can create tests for it.

A good place to start is KeyManager.loop_for_key

Fix test_allrecipes_com_plugin.py

Import from allrecipes.com doesn't work properly. Though it works somehow. This is a low priority task.

$ nosetests gourmet/tests/test_allrecipes_com_plugin.py
/home/kir/github/gourmet/gourmet/Undo.py:2: PyGIWarning: Gtk was imported without specifying a version first. Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
  from gi.repository import Gtk
E.
======================================================================
ERROR: test_parse (gourmet.tests.test_allrecipes_com_plugin.TestAllRecipesPlugin)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/kir/github/gourmet/gourmet/tests/test_allrecipes_com_plugin.py", line 48, in test_parse
    instructions = [r for r in result if r[1] == "instructions"][0][0].text
IndexError: list index out of range

Being built from source, gourmet cannot be started

If we clone gourmet from Github and do python setup.py install [--user], then it cannot be started.

The problem is that the path in uifile is incorrect. It looks like that's because base_dir which is currently set in settings.py as

base_dir = os.path.join(os.path.dirname(__file__), '..')

points one level up, which is correct for development, but it breaks execution for regular users.

It might be the case that this way of installation was not foreseen and had never been tested.

Clicking Cancel with unsaved changes in RecEditor blanks the RecEditor

Clicking Cancel with unsaved changes in the RecEditor will close the window, and will display a blank window when reopening it.

The cancel action should not close the window to begin with, but it seems that it increments the ingredient count, but is not saved in the IngredientController.imodel.

Below is a recoding showing the issue:
rec_editor_cancel

It's a rather easy fix: the issue can be mitigated by doing nothing on cancellation (ie. do nothing when handling UserCancelledError, as is done in the timer)

Fix test_importer.py

Somewhat related to #25:

 TypeError: catching classes that do not inherit from BaseException is not allowed

Priority: medium

Plugins menu item is not accessible

SettingsPlugins menu item is not accessible.

The issue looks trivial though, as always, no one knows how far it extends.

Traceback (most recent call last):
  File "./gourmet/GourmetRecipeManager.py", line 1113, in <lambda>
    lambda *args: plugin_gui.show_plugin_chooser()),
  File "./gourmet/plugin_gui.py", line 154, in show_plugin_chooser
    pc = PluginChooser()
  File "./gourmet/plugin_gui.py", line 20, in __init__
    self.add_labels()
  File "./gourmet/plugin_gui.py", line 37, in add_labels
    self.window.vbox.pack_start(head,expand=False)
TypeError: Gtk.Box.pack_start() takes exactly 5 non-keyword arguments (2 given)

Graciously handle unset locale

Upstream, someone reported that Gourmet does not handle unset locale.

I guess it has to do with gourmet/defaults/defaults.py, but since many changes were done since the previous release, the issue might be gone.

I consider it to be rather low priority.

Database search-object should be extract into dataclass/namedTuple & 'operator' should be an Enum

This ticket is a result of adding type annotations to 'get_ingkeys_with_count(self, search={})' signature in 'gourmet/backends/db.py'.

'get_ingkeys_with_count' now has an input parameter 'search: Optional[Mapping[str, Any]]' which is too lenient, as arbitrary string keys can be used and arbitrary values are valid, i.e. can take any type as its values. This effectively results in the static type checker not doing any type any checking.

As we only access a few known fieldnames, the search-Object should be extract into some namedTuple or dataclass.
'search: Optional[Mapping[str, Any]]', is not a proper mapping type, maybe it should be replaced by an Enum.

Also, the 'get_ingkeys_with_count' return type is 'List[Tuple[str, int]]', maybe the return type should be a dict.

Fix test_importManager.py

Like may other tests, it fails with

TypeError: catching classes that do not inherit from BaseException is not allowed

but it looks like an import problem.
Priority: medium

syntax error in setup.py line 290?

While building the flatpak, I got to the following error:

`Building module gourmet in /home/scott/Documents/flatpak/Gourmet2/.flatpak-builder/build/gourmet-1

Already on 'master'
Running: python setup.py install --prefix=/app
  File "setup.py", line 290
    print(line, end='')
                   ^
SyntaxError: invalid syntax
Error: module gourmet: Child process exited with code 1
`

Unused Functionality in key manager

There is quite a lot of unused, untested, and probably unworking functionality in the key manager. I identified at least the following. What do we want to do with it? Do we remove the code completely leaving us with less work in conversion and test creation or do we want to create tests and fixes and even the calls?

diff --git a/gourmet/keymanager.py b/gourmet/keymanager.py
index ec793895..d0a961e0 100644
--- a/gourmet/keymanager.py
+++ b/gourmet/keymanager.py
@@ -30,11 +30,6 @@ class KeyManager:
             from . import recipeManager
             rm = recipeManager.default_rec_manager()
         self.rm = rm
-        self.cooking_verbs=cooking_verbs
-        # This needs to be made sane i18n-wise
-        self.ignored = defaults.IGNORE
-        self.ignored.extend(self.cooking_verbs)
-        self.ignored_regexp = re.compile("[,; ]?(" + '|'.join(self.ignored) + ")[,; ]?")
         if self.rm.fetch_len(self.rm.keylookup_table) == 0:
             self.initialize_from_defaults()
         self.initialize_categories()
@@ -50,10 +45,6 @@ class KeyManager:
                     )
         self.rm.keylookup_table.insert().execute(dics)
 
-    def make_regexp_for_strings (self, ignored):
-        ret = "("
-        ignored.join("|")
-
     def regexp_for_all_words (self, txt):
         """Return a regexp to match any of the words in string."""
         regexp="(^|\W)("
@@ -233,33 +224,6 @@ class KeyManager:
         timer.end()
         return ingr
 
-    def sing_equal(self, str1, str2):
-        debug("Start sing_equal(self,%s,%s)"%(str1,str2),10)
-        sing_str1 = self.remove_final_s(str1)
-        sing_str2 = self.remove_final_s(str2)
-        return sing_str1 == sing_str2
-
-    def remove_verbs (self,words):
-        """Handed a list of words, we remove anything from the
-        list that matches a regexp in self.ignored"""
-        debug("Start remove_verbs",10)
-        t=TimeAction('remove_verbs',0)
-        stringp=True
-        if type(words)==type([]):
-            stringp=False
-            words = string.join(words," ")
-        words = words.split(';')[0] #we ignore everything after semicolon
-        words = words.split("--")[0] # we ignore everything after double dashes too!
-        m = self.ignored_regexp.match(words)
-        while m:
-            words = words[0:m.start()] + words[m.end():]
-            m = self.ignored_regexp.match(words)
-        t.end()
-        if stringp:
-            return words
-        else:
-            return words.split()
-
 
 class KeyDictionary:
     def __init__ (self, rm):
@@ -273,15 +237,6 @@ class KeyDictionary:
         elif k in self.default: return True
         else: return False
 
-    def srt_by_2nd (self, i1, i2):
-        """Sort by the reverse order of the second item in each of i1
-        and i2"""
-        if i1[1] < i2[1]:
-            return 1
-        if i2[1] < i1[1]:
-            return -1
-        else: return 0
-
     def __getitem__ (self, k):
         kvw = self.rm.fetch_count(
             self.rm.ingredients_table,
@@ -307,20 +262,6 @@ class KeyDictionary:
         lst.extend(list(self.default.items()))
         return lst
 
-cooking_verbs=["cored",
-               "peeled",
-               "sliced",
-               "chopped",
-               "diced",
-               "pureed",
-               "blended",
-               "grated",
-               "minced",
-               "cored",
-               "heated",
-               "warmed",
-               "chilled"]
-
 def get_keymanager (*args, **kwargs):
     try:
         return KeyManager(*args,**kwargs)

Replace regex parser with context free grammar parser

If you've looked at the offending code behind #79 , you'll see that the almagamation of regexes looks like an unmaintainable mess. Especially string-replacement in regex-strings and other combination mechanisms seem dangerous.

Now python regexes are not combinable by usual regex operation and I don't know of a performant regex engine that allows that.

A maintainable alternative could be using a parser/tokenizer combination out of the realm of context-free grammars. In my experience, they can be combined more easily and are therefore more maintainable. The result is a tree instead of a list, which is no problem. Some Tree-nodes we would replace by data constructors (like creating decimals from a comma-seperator and two number-nodes) and other parts of the tree would be flattened to a list, basically as it is now.

I think I have also more confidence in me/us writing/reading the simple regexes for tokens and a context-free grammar than whole regexes for complex expressions that span multiple lines and are later changed through string-processing functions.

In the longer run we could make chomsky proud and even supply some general CFGs for the supported languages and identify verbs, adjectives, and nouns through the grammar, giving us more confidence in extracting recipe steps.

I know it's not a priority but is this something we would put on the agenda?

Common issues we have to look out for

This is just a meta-issue listing the issues we have to look out for, that weren't autotranslated from python 2 to 3

  • GObject constructors aren't correctly translated (see #5)
  • Many functions expect int arguments. In python2 a / division between two int always resulted in int. In python3 the result is always float. We have to replace / with // where appropriate. (see #6)
  • old-style sort-functions are in use (List.sort()). These need to be replaced with key functions (see #9 / #7)

Please help add to this list and keep it in mind when touching up code.

Refactor tests

There are quite a number of unit tests spread around the code base. Since we have a working CI now, it might be useful to collect all the tests in one place. Namely, in gourmet/tests folder.

Note that there is a test suite https://github.com/kirienko/gourmet/blob/master/gourmet/tests/test_gourmet.py (also broken) which one might to use instead. Though it doesn't look like a good idea, because one need to update it manually with every new test written. Probably it is better to refactor this out.

Fix test_reccard.py

The error message is the same as in #22:

AttributeError: module 'gourmet.gglobals' has no attribute 'dbargs'

But the problem might be completely different.
That's also an old and complicated test.

Priority: high

Fix test_importers.py

Looks very similar to #27, but might become more difficult.

3 tests out of 3 are failing:

TypeError: catching classes that do not inherit from BaseException is not allowed

Type annotations?

Up for debate: as we touch up some internal functions and helper methods like I propose in #14, do we want to make mypy-compatible type-annotations while we're at it? If we start from the functions small enough, we might be able to type at least the helper modules.

What do you think?

GtkComboBoxText should be editable

ComboboxText are used for all drop down menus.

However, these are not editable, in places where I think they ought to be, such as the yielded portions units, categories, cuisine, and sources in the record editor.

image

I'm not sure it historically was handled. Could someone shed some light on the matter?
Thanks!

Ubuntu 20.04 required for CI

In the CI, the version of Ubuntu currently targeted is ubuntu-latest, which is 18.04 under the hood.

In this version of Ubuntu, the provided version of PyGObject does not have the key feature Pango.AttrList.get_iterator(), used by many parts of the application to where free-text is accepted (eg. entering the instructions of a recipe).

We can test this by doing the following:

In [1]: import gi

In [2]: gi.require_versions({'Gtk': '3.0', 'Pango': '1.0'})

In [3]: from gi.repository import Pango

In [4]: hasattr(Pango.AttrList,'get_iterator')
Out[4]: False

Ubuntu 20.04 has the latest version of libpango, and I confirmed that this test passes.

We are aware that Github is working on getting ubuntu-latest to be 20.04, but it's not there yet.

As such, most test won't pass, even if they're correct.

As we aim to target the libraries of Ubuntu 20.04 or equivalent, I suggest to wait it off until ubuntu-latest is 20.04, as we have plenty to do before anyway.

However, we are open to a contribution that would update the CI to make use of Ubuntu 20.04! 😄

Note that we have not tried tested this behaviour on Ubuntu 19.04.
Users of the latest Linux Mint (Tricia), will also run into this issue.

Make languages into classes

Currently, languages from gourmet/defaults/ are dynamically included modules that all behave the same. I don't think, we can get mypy to correctly type them. As it is just a set of fields and two methods that are all there by convention, I propose we change that in the language files to a (static) class that gets exported by gourmet/defaults/defaults.py. This wouldn't change the interface, but would change the type from just module to LanguageClass, allowing mypy to properly inspect that.

If you agree, I would be willing to do that and then I could start cleaning up after other people's improvements to get the type hints at the interfaces correctly.

Fix test_foodnetwork_plugin.py

1 failed, 1 passed, 10 warnings

The failure is related to the BeautifulSoup4 changes:

AttributeError: type object 'BeautifulSoup' has no attribute 'BeautifulSoup'

Priority: low

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.