Spaces:
				
			
			
	
			
			
		Runtime error
		
	
	
	
			
			
	
	
	
	
		
		
		Runtime error
		
	Transfer from git - HC
Browse files- CONTRIBUTING.md +35 -0
- README.md +226 -3
- app.py +29 -111
- asset/css/style.css +58 -9
- meta.py +7 -6
- Build Ingredients Vocab.ipynb → notes/Build Ingredients Vocab.ipynb +0 -0
- utils/__init__.py +0 -0
- utils/api.py +26 -0
- utils/draw.py +86 -0
- utils/ext.py +43 -0
- utils/st.py +10 -0
- utils/utils.py +73 -0
    	
        CONTRIBUTING.md
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            1. Fork the repository by clicking on the ``Fork`` button on the repository's page. This creates a copy of the code under your GitHub user account.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            2. Clone your fork to your local disk, and add the base repository as a remote.
         | 
| 4 | 
            +
            ```bash
         | 
| 5 | 
            +
            $ git clone git@github.com:<your-GitHub-username>/chef-transformer.git
         | 
| 6 | 
            +
            $ cd chef-transformer
         | 
| 7 | 
            +
            $ git remote add upstream https://github.com/chef-transformer/chef-transformer.git
         | 
| 8 | 
            +
            ```
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            3. Create a new branch to hold your development changes.
         | 
| 11 | 
            +
            ```bash
         | 
| 12 | 
            +
            $ git checkout -b a-descriptive-name-for-your-changes
         | 
| 13 | 
            +
            ```
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            > NOTE: Do not work on the ``main`` branch.
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            4. Set up a development environment by running the following command in a virtual environment.
         | 
| 18 | 
            +
            ```bash
         | 
| 19 | 
            +
            $ pip install -r requirements.txt
         | 
| 20 | 
            +
            ```
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            5. DEVELOP THE CODE
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            6. It is a good idea to sync your copy of the code with the original repository regularly. This way you can quickly account for changes.
         | 
| 25 | 
            +
            ```bash
         | 
| 26 | 
            +
            $ git fetch upstream
         | 
| 27 | 
            +
            $ git rebase upstream/main
         | 
| 28 | 
            +
            ```
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            7. Push the changes to your account using:
         | 
| 31 | 
            +
            ```bash
         | 
| 32 | 
            +
            $ git push -u origin a-descriptive-name-for-your-changes
         | 
| 33 | 
            +
            ```
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            8. Once you are satisfied (and the checklist above is happy too), go to the webpage of your fork on GitHub. Click on ``Pull Request`` to send your changes to the project maintainers for review.
         | 
    	
        README.md
    CHANGED
    
    | @@ -8,10 +8,233 @@ app_file: app.py | |
| 8 | 
             
            pinned: false
         | 
| 9 | 
             
            ---
         | 
| 10 |  | 
| 11 | 
            -
            #  | 
|  | |
| 12 |  | 
|  | |
| 13 |  | 
| 14 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 15 | 
             
            ```
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 16 | 
             
            streamlit run app.py
         | 
| 17 | 
            -
            ```
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 8 | 
             
            pinned: false
         | 
| 9 | 
             
            ---
         | 
| 10 |  | 
| 11 | 
            +
            # Chef Transformer (T5) 
         | 
| 12 | 
            +
            > This is part of the [Flax/Jax Community Week](https://discuss.huggingface.co/t/recipe-generation-model/7475), organized by [HuggingFace](https://huggingface.co/) and TPU usage sponsored by Google.
         | 
| 13 |  | 
| 14 | 
            +
            Want to give it a try? Then what's the wait, head over to the demo [here](https://share.streamlit.io/chef-transformer/chef-transformer/main/app.py).
         | 
| 15 |  | 
| 16 | 
            +
             | 
| 17 | 
            +
            ## Team Members
         | 
| 18 | 
            +
            - Mehrdad Farahani ([m3hrdadfi](https://huggingface.co/m3hrdadfi))
         | 
| 19 | 
            +
            - Kartik Godawat ([dk-crazydiv](https://huggingface.co/dk-crazydiv))
         | 
| 20 | 
            +
            - Haswanth Aekula ([hassiahk](https://huggingface.co/hassiahk))
         | 
| 21 | 
            +
            - Deepak Pandian ([rays2pix](https://huggingface.co/rays2pix))
         | 
| 22 | 
            +
            - Nicholas Broad ([nbroad](https://huggingface.co/nbroad))
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            ## Dataset
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            [RecipeNLG: A Cooking Recipes Dataset for Semi-Structured Text Generation](https://recipenlg.cs.put.poznan.pl/). This dataset contains **2,231,142** cooking recipes (>2 millions) with size of **2.14 GB**. It's processed in more careful way.
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ### Example
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ```json
         | 
| 31 | 
            +
            {
         | 
| 32 | 
            +
                "NER": [
         | 
| 33 | 
            +
                    "oyster crackers",
         | 
| 34 | 
            +
                    "salad dressing",
         | 
| 35 | 
            +
                    "lemon pepper",
         | 
| 36 | 
            +
                    "dill weed",
         | 
| 37 | 
            +
                    "garlic powder",
         | 
| 38 | 
            +
                    "salad oil"
         | 
| 39 | 
            +
                ],
         | 
| 40 | 
            +
                "directions": [
         | 
| 41 | 
            +
                    "Combine salad dressing mix and oil.",
         | 
| 42 | 
            +
                    "Add dill weed, garlic powder and lemon pepper.",
         | 
| 43 | 
            +
                    "Pour over crackers; stir to coat.",
         | 
| 44 | 
            +
                    "Place in warm oven.",
         | 
| 45 | 
            +
                    "Use very low temperature for 15 to 20 minutes."
         | 
| 46 | 
            +
                ],
         | 
| 47 | 
            +
                "ingredients": [
         | 
| 48 | 
            +
                    "12 to 16 oz. plain oyster crackers",
         | 
| 49 | 
            +
                    "1 pkg. Hidden Valley Ranch salad dressing mix",
         | 
| 50 | 
            +
                    "1/4 tsp. lemon pepper",
         | 
| 51 | 
            +
                    "1/2 to 1 tsp. dill weed",
         | 
| 52 | 
            +
                    "1/4 tsp. garlic powder",
         | 
| 53 | 
            +
                    "3/4 to 1 c. salad oil"
         | 
| 54 | 
            +
                ],
         | 
| 55 | 
            +
                "link": "www.cookbooks.com/Recipe-Details.aspx?id=648947",
         | 
| 56 | 
            +
                "source": "Gathered",
         | 
| 57 | 
            +
                "title": "Hidden Valley Ranch Oyster Crackers"
         | 
| 58 | 
            +
            }
         | 
| 59 | 
             
            ```
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            ## How To Use
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            ```bash
         | 
| 64 | 
            +
            # Installing requirements
         | 
| 65 | 
            +
            pip install transformers
         | 
| 66 | 
            +
            ```
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            ```python
         | 
| 69 | 
            +
            from transformers import FlaxAutoModelForSeq2SeqLM
         | 
| 70 | 
            +
            from transformers import AutoTokenizer
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            MODEL_NAME_OR_PATH = "flax-community/t5-recipe-generation"
         | 
| 73 | 
            +
            tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME_OR_PATH, use_fast=True)
         | 
| 74 | 
            +
            model = FlaxAutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME_OR_PATH)
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            prefix = "items: "
         | 
| 77 | 
            +
            # generation_kwargs = {
         | 
| 78 | 
            +
            #     "max_length": 1024,
         | 
| 79 | 
            +
            #     "min_length": 128,
         | 
| 80 | 
            +
            #     "no_repeat_ngram_size": 3,
         | 
| 81 | 
            +
            #     "do_sample": True,
         | 
| 82 | 
            +
            #     "top_k": 60,
         | 
| 83 | 
            +
            #     "top_p": 0.95
         | 
| 84 | 
            +
            # }
         | 
| 85 | 
            +
            generation_kwargs = {
         | 
| 86 | 
            +
                "max_length": 512,
         | 
| 87 | 
            +
                "min_length": 64,
         | 
| 88 | 
            +
                "no_repeat_ngram_size": 3,
         | 
| 89 | 
            +
                "early_stopping": True,
         | 
| 90 | 
            +
                "num_beams": 5,
         | 
| 91 | 
            +
                "length_penalty": 1.5,
         | 
| 92 | 
            +
            }
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            special_tokens = tokenizer.all_special_tokens
         | 
| 95 | 
            +
            tokens_map = {
         | 
| 96 | 
            +
                "<sep>": "--",
         | 
| 97 | 
            +
                "<section>": "\n"
         | 
| 98 | 
            +
            }
         | 
| 99 | 
            +
            def skip_special_tokens(text, special_tokens):
         | 
| 100 | 
            +
                for token in special_tokens:
         | 
| 101 | 
            +
                    text = text.replace(token, "")
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                return text
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            def target_postprocessing(texts, special_tokens):
         | 
| 106 | 
            +
                if not isinstance(texts, list):
         | 
| 107 | 
            +
                    texts = [texts]
         | 
| 108 | 
            +
                
         | 
| 109 | 
            +
                new_texts = []
         | 
| 110 | 
            +
                for text in texts:
         | 
| 111 | 
            +
                    text = skip_special_tokens(text, special_tokens)
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    for k, v in tokens_map.items():
         | 
| 114 | 
            +
                        text = text.replace(k, v)
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    new_texts.append(text)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                return new_texts
         | 
| 119 | 
            +
             | 
| 120 | 
            +
            def generation_function(texts):
         | 
| 121 | 
            +
                _inputs = texts if isinstance(texts, list) else [texts]
         | 
| 122 | 
            +
                inputs = [prefix + inp for inp in _inputs]
         | 
| 123 | 
            +
                inputs = tokenizer(
         | 
| 124 | 
            +
                    inputs, 
         | 
| 125 | 
            +
                    max_length=256, 
         | 
| 126 | 
            +
                    padding="max_length", 
         | 
| 127 | 
            +
                    truncation=True, 
         | 
| 128 | 
            +
                    return_tensors="jax"
         | 
| 129 | 
            +
                )
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                input_ids = inputs.input_ids
         | 
| 132 | 
            +
                attention_mask = inputs.attention_mask
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                output_ids = model.generate(
         | 
| 135 | 
            +
                    input_ids=input_ids, 
         | 
| 136 | 
            +
                    attention_mask=attention_mask,
         | 
| 137 | 
            +
                    **generation_kwargs
         | 
| 138 | 
            +
                )
         | 
| 139 | 
            +
                generated = output_ids.sequences
         | 
| 140 | 
            +
                generated_recipe = target_postprocessing(
         | 
| 141 | 
            +
                    tokenizer.batch_decode(generated, skip_special_tokens=False),
         | 
| 142 | 
            +
                    special_tokens
         | 
| 143 | 
            +
                )
         | 
| 144 | 
            +
                return generated_recipe
         | 
| 145 | 
            +
            ```
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            ```python
         | 
| 148 | 
            +
            items = [
         | 
| 149 | 
            +
                "macaroni, butter, salt, bacon, milk, flour, pepper, cream corn",
         | 
| 150 | 
            +
                "provolone cheese, bacon, bread, ginger"
         | 
| 151 | 
            +
            ]
         | 
| 152 | 
            +
            generated = generation_function(items)
         | 
| 153 | 
            +
            for text in generated:
         | 
| 154 | 
            +
                sections = text.split("\n")
         | 
| 155 | 
            +
                for section in sections:
         | 
| 156 | 
            +
                    section = section.strip()
         | 
| 157 | 
            +
                    if section.startswith("title:"):
         | 
| 158 | 
            +
                        section = section.replace("title:", "")
         | 
| 159 | 
            +
                        headline = "TITLE"
         | 
| 160 | 
            +
                    elif section.startswith("ingredients:"):
         | 
| 161 | 
            +
                        section = section.replace("ingredients:", "")
         | 
| 162 | 
            +
                        headline = "INGREDIENTS"
         | 
| 163 | 
            +
                    elif section.startswith("directions:"):
         | 
| 164 | 
            +
                        section = section.replace("directions:", "")
         | 
| 165 | 
            +
                        headline = "DIRECTIONS"
         | 
| 166 | 
            +
                    
         | 
| 167 | 
            +
                    if headline == "TITLE":
         | 
| 168 | 
            +
                        print(f"[{headline}]: {section.strip().capitalize()}")
         | 
| 169 | 
            +
                    else:
         | 
| 170 | 
            +
                        section_info = [f"  - {i+1}: {info.strip().capitalize()}" for i, info in enumerate(section.split("--"))]
         | 
| 171 | 
            +
                        print(f"[{headline}]:")
         | 
| 172 | 
            +
                        print("\n".join(section_info))
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                print("-" * 130)
         | 
| 175 | 
            +
            ```
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            Output:
         | 
| 178 | 
            +
            ```text
         | 
| 179 | 
            +
            [TITLE]: Macaroni and corn
         | 
| 180 | 
            +
            [INGREDIENTS]:
         | 
| 181 | 
            +
              - 1: 2 c. macaroni
         | 
| 182 | 
            +
              - 2: 2 tbsp. butter
         | 
| 183 | 
            +
              - 3: 1 tsp. salt
         | 
| 184 | 
            +
              - 4: 4 slices bacon
         | 
| 185 | 
            +
              - 5: 2 c. milk
         | 
| 186 | 
            +
              - 6: 2 tbsp. flour
         | 
| 187 | 
            +
              - 7: 1/4 tsp. pepper
         | 
| 188 | 
            +
              - 8: 1 can cream corn
         | 
| 189 | 
            +
            [DIRECTIONS]:
         | 
| 190 | 
            +
              - 1: Cook macaroni in boiling salted water until tender.
         | 
| 191 | 
            +
              - 2: Drain.
         | 
| 192 | 
            +
              - 3: Melt butter in saucepan.
         | 
| 193 | 
            +
              - 4: Blend in flour, salt and pepper.
         | 
| 194 | 
            +
              - 5: Add milk all at once.
         | 
| 195 | 
            +
              - 6: Cook and stir until thickened and bubbly.
         | 
| 196 | 
            +
              - 7: Stir in corn and bacon.
         | 
| 197 | 
            +
              - 8: Pour over macaroni and mix well.
         | 
| 198 | 
            +
            ----------------------------------------------------------------------------------------------------------------------------------
         | 
| 199 | 
            +
            [TITLE]: Grilled provolone and bacon sandwich
         | 
| 200 | 
            +
            [INGREDIENTS]:
         | 
| 201 | 
            +
              - 1: 2 slices provolone cheese
         | 
| 202 | 
            +
              - 2: 2 slices bacon
         | 
| 203 | 
            +
              - 3: 2 slices sourdough bread
         | 
| 204 | 
            +
              - 4: 2 slices pickled ginger
         | 
| 205 | 
            +
            [DIRECTIONS]:
         | 
| 206 | 
            +
              - 1: Place a slice of provolone cheese on one slice of bread.
         | 
| 207 | 
            +
              - 2: Top with a slice of bacon.
         | 
| 208 | 
            +
              - 3: Top with a slice of pickled ginger.
         | 
| 209 | 
            +
              - 4: Top with the other slice of bread.
         | 
| 210 | 
            +
              - 5: Heat a skillet over medium heat.
         | 
| 211 | 
            +
              - 6: Place the sandwich in the skillet and cook until the cheese is melted and the bread is golden brown.
         | 
| 212 | 
            +
            ----------------------------------------------------------------------------------------------------------------------------------
         | 
| 213 | 
            +
            ```
         | 
| 214 | 
            +
             | 
| 215 | 
            +
            ## Evaluation
         | 
| 216 | 
            +
             | 
| 217 | 
            +
            The following table summarizes the scores obtained by the **Chef Transformer**. Those marked as (*) are the baseline models.
         | 
| 218 | 
            +
             | 
| 219 | 
            +
            |      Model      |  WER  | COSIM | ROUGE-2 |
         | 
| 220 | 
            +
            | :-------------: | :---: | :---: | :-----: |
         | 
| 221 | 
            +
            |   Recipe1M+ *   | 0.786 | 0.589 |    -    |
         | 
| 222 | 
            +
            |   RecipeNLG *   | 0.751 | 0.666 |    -    |
         | 
| 223 | 
            +
            | ChefTransformer | 0.709 | 0.714 |  0.290  |
         | 
| 224 | 
            +
             | 
| 225 | 
            +
            ## Streamlit demo
         | 
| 226 | 
            +
             | 
| 227 | 
            +
            ```bash
         | 
| 228 | 
             
            streamlit run app.py
         | 
| 229 | 
            +
            ```
         | 
| 230 | 
            +
             | 
| 231 | 
            +
            ## Looking to contribute?
         | 
| 232 | 
            +
            Then follow the steps mentioned in this [contributing guide](CONTRIBUTING.md) and you are good to go.
         | 
| 233 | 
            +
             | 
| 234 | 
            +
            ## Copyright
         | 
| 235 | 
            +
             | 
| 236 | 
            +
            Special thanks to those who provided these fantastic materials.
         | 
| 237 | 
            +
            - [Anatomy](https://www.flaticon.com/free-icon)
         | 
| 238 | 
            +
            - [Chef Hat](https://www.vecteezy.com/members/jellyfishwater)
         | 
| 239 | 
            +
            - [Moira Nazzari](https://pixabay.com/photos/food-dessert-cake-eggs-butter-3048440/)
         | 
| 240 | 
            +
            - [Instagram Post](https://www.freepik.com/free-psd/recipes-ad-social-media-post-template_11520617.htm)
         | 
    	
        app.py
    CHANGED
    
    | @@ -18,9 +18,15 @@ import textwrap | |
| 18 | 
             
            from examples import EXAMPLES
         | 
| 19 | 
             
            import dummy
         | 
| 20 | 
             
            import meta
         | 
| 21 | 
            -
            from utils import  | 
|  | |
|  | |
|  | |
| 22 | 
             
                remote_css,
         | 
| 23 | 
             
                local_css,
         | 
|  | |
|  | |
|  | |
| 24 | 
             
                load_image_from_url,
         | 
| 25 | 
             
                load_image_from_local,
         | 
| 26 | 
             
                image_to_base64,
         | 
| @@ -28,104 +34,6 @@ from utils import ( | |
| 28 | 
             
            )
         | 
| 29 |  | 
| 30 |  | 
| 31 | 
            -
            def generate_cook_image(query, app_id, app_key):
         | 
| 32 | 
            -
                api_url = f"https://api.edamam.com/api/recipes/v2?type=public&q={query}&app_id={app_id}&app_key={app_key}&field=image"
         | 
| 33 | 
            -
             | 
| 34 | 
            -
                try:
         | 
| 35 | 
            -
                    r = requests.get(api_url)
         | 
| 36 | 
            -
                    if r.status_code != 200:
         | 
| 37 | 
            -
                        return None
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                    rj = r.json()
         | 
| 40 | 
            -
                    if "hits" not in rj or not len(rj["hits"]) > 0:
         | 
| 41 | 
            -
                        return None
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                    data = rj["hits"]
         | 
| 44 | 
            -
                    data = data[random.randint(1, min(5, len(data) - 1))] if len(data) > 1 else data[0]
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                    if "recipe" not in data or "image" not in data["recipe"]:
         | 
| 47 | 
            -
                        return None
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                    image = data["recipe"]["image"]
         | 
| 50 | 
            -
                    return image
         | 
| 51 | 
            -
                except Exception as e:
         | 
| 52 | 
            -
                    return None
         | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
            def generate_food_with_logo_image(bg_path, logo_path, food_url, no_food="asset/frame/no_food.png"):
         | 
| 56 | 
            -
                bg = Image.open(bg_path)
         | 
| 57 | 
            -
                width, height = bg.size
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                logo = Image.open(logo_path)
         | 
| 60 | 
            -
                logo_width, logo_height, logo_ratio, logo_rb, logo_mb = logo.size + (3, -20, 45)
         | 
| 61 | 
            -
                logo_width, logo_height = (logo_width // logo_ratio, logo_height // logo_ratio)
         | 
| 62 | 
            -
                logo = logo.resize((logo_width, logo_height))
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                food = load_image_from_url(food_url, rgba_mode=True, default_image=no_food)
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                food_width, food_height = (300, 300)
         | 
| 67 | 
            -
                food = food.resize((food_width, food_height))
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                bg.paste(food, (0, 0), food)
         | 
| 70 | 
            -
                bg.paste(logo, (width - logo_width - logo_rb, height - logo_height - logo_mb), logo)
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                return bg
         | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
            def generate_recipe_image(
         | 
| 76 | 
            -
                    recipe_data,
         | 
| 77 | 
            -
                    bg_path,
         | 
| 78 | 
            -
                    food_logo_ia,
         | 
| 79 | 
            -
                    fonts,
         | 
| 80 | 
            -
                    bg_color="#ffffff"
         | 
| 81 | 
            -
            ):
         | 
| 82 | 
            -
                bg = Image.open(bg_path)
         | 
| 83 | 
            -
                bg.paste(food_logo_ia, (50, 50), food_logo_ia)
         | 
| 84 | 
            -
                bg_color = Image.new("RGBA", bg.size, bg_color)
         | 
| 85 | 
            -
                bg_color.paste(bg, mask=bg)
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                im_editable = ImageDraw.Draw(bg_color)
         | 
| 88 | 
            -
                im_editable.text(
         | 
| 89 | 
            -
                    (418, 30),
         | 
| 90 | 
            -
                    textwrap.fill(recipe_data["title"], 15).replace(" \n", "\n"),
         | 
| 91 | 
            -
                    (61, 61, 70),
         | 
| 92 | 
            -
                    font=fonts["title"],
         | 
| 93 | 
            -
                )
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                im_editable.text(
         | 
| 96 | 
            -
                    (100, 450),
         | 
| 97 | 
            -
                    "Ingredients",
         | 
| 98 | 
            -
                    (61, 61, 70),
         | 
| 99 | 
            -
                    font=fonts["body_bold"],
         | 
| 100 | 
            -
                )
         | 
| 101 | 
            -
                ingredients = recipe_data["ingredients"]
         | 
| 102 | 
            -
                ingredients = [textwrap.fill(item, 30).replace("\n", "\n   ") for item in ingredients]
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                im_editable.text(
         | 
| 105 | 
            -
                    (50, 520),
         | 
| 106 | 
            -
                    "\n".join([f"- {item}" for item in ingredients]),
         | 
| 107 | 
            -
                    (61, 61, 70),
         | 
| 108 | 
            -
                    font=fonts["body"],
         | 
| 109 | 
            -
                )
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                im_editable.text(
         | 
| 112 | 
            -
                    (700, 450),
         | 
| 113 | 
            -
                    "Directions",
         | 
| 114 | 
            -
                    (61, 61, 70),
         | 
| 115 | 
            -
                    font=fonts["body_bold"],
         | 
| 116 | 
            -
                )
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                directions = recipe_data["directions"]
         | 
| 119 | 
            -
                directions = [textwrap.fill(item, 70).replace("\n", "\n   ") for item in directions]
         | 
| 120 | 
            -
                im_editable.text(
         | 
| 121 | 
            -
                    (430, 520),
         | 
| 122 | 
            -
                    "\n".join([f"{i + 1}. {item}" for i, item in enumerate(directions)]).strip(),
         | 
| 123 | 
            -
                    (61, 61, 70),
         | 
| 124 | 
            -
                    font=fonts["body"],
         | 
| 125 | 
            -
                )
         | 
| 126 | 
            -
                return bg_color
         | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
             
            class TextGeneration:
         | 
| 130 | 
             
                def __init__(self):
         | 
| 131 | 
             
                    self.debug = False
         | 
| @@ -215,6 +123,7 @@ class TextGeneration: | |
| 215 | 
             
                    return frame
         | 
| 216 |  | 
| 217 | 
             
                def generate(self, items, generation_kwargs):
         | 
|  | |
| 218 | 
             
                    recipe = self.dummy_outputs[random.randint(0, len(self.dummy_outputs) - 1)]
         | 
| 219 |  | 
| 220 | 
             
                    if not self.debug:
         | 
| @@ -291,9 +200,10 @@ def main(): | |
| 291 | 
             
                # else:
         | 
| 292 | 
             
                #     get_random_frame = generator.frames[0]
         | 
| 293 |  | 
|  | |
| 294 | 
             
                local_css("asset/css/style.css")
         | 
| 295 |  | 
| 296 | 
            -
                col1, col2 = st.beta_columns([ | 
| 297 | 
             
                with col2:
         | 
| 298 | 
             
                    st.image(load_image_from_local("asset/images/chef-transformer-transparent.png"), width=300)
         | 
| 299 | 
             
                    st.markdown(meta.SIDEBAR_INFO, unsafe_allow_html=True)
         | 
| @@ -321,12 +231,13 @@ def main(): | |
| 321 | 
             
                        prompt_box = EXAMPLES[prompt]
         | 
| 322 |  | 
| 323 | 
             
                    items = st.text_area(
         | 
| 324 | 
            -
                        'Insert your  | 
| 325 | 
             
                        pure_comma_separation(prompt_box, return_list=False),
         | 
| 326 | 
             
                    )
         | 
| 327 | 
             
                    items = pure_comma_separation(items, return_list=False)
         | 
| 328 | 
             
                    entered_items = st.empty()
         | 
| 329 | 
            -
             | 
|  | |
| 330 |  | 
| 331 | 
             
                st.markdown(
         | 
| 332 | 
             
                    "<hr />",
         | 
| @@ -354,14 +265,21 @@ def main(): | |
| 354 | 
             
                            food_image = generated_recipe["image"]
         | 
| 355 | 
             
                            food_image = load_image_from_url(food_image, rgba_mode=True, default_image=generator.no_food)
         | 
| 356 | 
             
                            food_image = image_to_base64(food_image)
         | 
| 357 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
| 358 | 
             
                            directions = [textwrap.fill(item, 70).replace("\n", "\n   ") for item in
         | 
| 359 | 
             
                                          generated_recipe["directions"]]
         | 
|  | |
|  | |
| 360 | 
             
                            generated_recipe["by"] = chef
         | 
| 361 |  | 
| 362 | 
            -
                            r1, r2 = st.beta_columns([ | 
| 363 |  | 
| 364 | 
            -
                            with  | 
| 365 | 
             
                                # st.write(st.session_state.get_random_frame)
         | 
| 366 | 
             
                                # if hasattr(st, "session_state"):
         | 
| 367 | 
             
                                #     recipe_post = generator.generate_frame(generated_recipe, st.session_state.get_random_frame)
         | 
| @@ -378,21 +296,21 @@ def main(): | |
| 378 | 
             
                                    output_format="PNG"
         | 
| 379 | 
             
                                )
         | 
| 380 |  | 
| 381 | 
            -
                            with  | 
| 382 | 
             
                                st.markdown(
         | 
| 383 | 
             
                                    " ".join([
         | 
| 384 | 
             
                                        "<div class='r-text-recipe'>",
         | 
| 385 | 
             
                                        "<div class='food-title'>",
         | 
| 386 | 
             
                                        f"<img src='{food_image}' />",
         | 
| 387 | 
            -
                                        f"<h2>{title}</h2>",
         | 
| 388 | 
             
                                        "</div>",
         | 
| 389 | 
             
                                        '<div class="divider"><div class="divider-mask"></div></div>',
         | 
| 390 | 
            -
                                        "<h3>Ingredients</h3>",
         | 
| 391 | 
            -
                                        "<ul class='ingredients-list'>",
         | 
| 392 | 
             
                                        " ".join([f'<li>{item}</li>' for item in ingredients]),
         | 
| 393 | 
             
                                        "</ul>",
         | 
| 394 | 
            -
                                        "<h3>Directions</h3>",
         | 
| 395 | 
            -
                                        "<ol class='ingredients-list'>",
         | 
| 396 | 
             
                                        " ".join([f'<li>{item}</li>' for item in directions]),
         | 
| 397 | 
             
                                        "</ol>",
         | 
| 398 | 
             
                                        "</div>"
         | 
|  | |
| 18 | 
             
            from examples import EXAMPLES
         | 
| 19 | 
             
            import dummy
         | 
| 20 | 
             
            import meta
         | 
| 21 | 
            +
            from utils import ext
         | 
| 22 | 
            +
            from utils.api import generate_cook_image
         | 
| 23 | 
            +
            from utils.draw import generate_food_with_logo_image, generate_recipe_image
         | 
| 24 | 
            +
            from utils.st import (
         | 
| 25 | 
             
                remote_css,
         | 
| 26 | 
             
                local_css,
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            )
         | 
| 29 | 
            +
            from utils.utils import (
         | 
| 30 | 
             
                load_image_from_url,
         | 
| 31 | 
             
                load_image_from_local,
         | 
| 32 | 
             
                image_to_base64,
         | 
|  | |
| 34 | 
             
            )
         | 
| 35 |  | 
| 36 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 37 | 
             
            class TextGeneration:
         | 
| 38 | 
             
                def __init__(self):
         | 
| 39 | 
             
                    self.debug = False
         | 
|  | |
| 123 | 
             
                    return frame
         | 
| 124 |  | 
| 125 | 
             
                def generate(self, items, generation_kwargs):
         | 
| 126 | 
            +
                    recipe = self.dummy_outputs[0]
         | 
| 127 | 
             
                    recipe = self.dummy_outputs[random.randint(0, len(self.dummy_outputs) - 1)]
         | 
| 128 |  | 
| 129 | 
             
                    if not self.debug:
         | 
|  | |
| 200 | 
             
                # else:
         | 
| 201 | 
             
                #     get_random_frame = generator.frames[0]
         | 
| 202 |  | 
| 203 | 
            +
                remote_css("https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600&family=Poppins:wght@600&display=swap")
         | 
| 204 | 
             
                local_css("asset/css/style.css")
         | 
| 205 |  | 
| 206 | 
            +
                col1, col2 = st.beta_columns([6, 4])
         | 
| 207 | 
             
                with col2:
         | 
| 208 | 
             
                    st.image(load_image_from_local("asset/images/chef-transformer-transparent.png"), width=300)
         | 
| 209 | 
             
                    st.markdown(meta.SIDEBAR_INFO, unsafe_allow_html=True)
         | 
|  | |
| 231 | 
             
                        prompt_box = EXAMPLES[prompt]
         | 
| 232 |  | 
| 233 | 
             
                    items = st.text_area(
         | 
| 234 | 
            +
                        'Insert your food items here (separated by `,`): ',
         | 
| 235 | 
             
                        pure_comma_separation(prompt_box, return_list=False),
         | 
| 236 | 
             
                    )
         | 
| 237 | 
             
                    items = pure_comma_separation(items, return_list=False)
         | 
| 238 | 
             
                    entered_items = st.empty()
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                recipe_button = st.button('Get Recipe!')
         | 
| 241 |  | 
| 242 | 
             
                st.markdown(
         | 
| 243 | 
             
                    "<hr />",
         | 
|  | |
| 265 | 
             
                            food_image = generated_recipe["image"]
         | 
| 266 | 
             
                            food_image = load_image_from_url(food_image, rgba_mode=True, default_image=generator.no_food)
         | 
| 267 | 
             
                            food_image = image_to_base64(food_image)
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                            ingredients = ext.ingredients(
         | 
| 270 | 
            +
                                generated_recipe["ingredients"],
         | 
| 271 | 
            +
                                pure_comma_separation(items, return_list=True)
         | 
| 272 | 
            +
                            )
         | 
| 273 | 
            +
             | 
| 274 | 
             
                            directions = [textwrap.fill(item, 70).replace("\n", "\n   ") for item in
         | 
| 275 | 
             
                                          generated_recipe["directions"]]
         | 
| 276 | 
            +
                            directions = ext.directions(directions)
         | 
| 277 | 
            +
             | 
| 278 | 
             
                            generated_recipe["by"] = chef
         | 
| 279 |  | 
| 280 | 
            +
                            r1, r2 = st.beta_columns([6, 2])
         | 
| 281 |  | 
| 282 | 
            +
                            with r2:
         | 
| 283 | 
             
                                # st.write(st.session_state.get_random_frame)
         | 
| 284 | 
             
                                # if hasattr(st, "session_state"):
         | 
| 285 | 
             
                                #     recipe_post = generator.generate_frame(generated_recipe, st.session_state.get_random_frame)
         | 
|  | |
| 296 | 
             
                                    output_format="PNG"
         | 
| 297 | 
             
                                )
         | 
| 298 |  | 
| 299 | 
            +
                            with r1:
         | 
| 300 | 
             
                                st.markdown(
         | 
| 301 | 
             
                                    " ".join([
         | 
| 302 | 
             
                                        "<div class='r-text-recipe'>",
         | 
| 303 | 
             
                                        "<div class='food-title'>",
         | 
| 304 | 
             
                                        f"<img src='{food_image}' />",
         | 
| 305 | 
            +
                                        f"<h2 class='font-title text-bold'>{title}</h2>",
         | 
| 306 | 
             
                                        "</div>",
         | 
| 307 | 
             
                                        '<div class="divider"><div class="divider-mask"></div></div>',
         | 
| 308 | 
            +
                                        "<h3 class='ingredients font-body text-bold'>Ingredients</h3>",
         | 
| 309 | 
            +
                                        "<ul class='ingredients-list font-body'>",
         | 
| 310 | 
             
                                        " ".join([f'<li>{item}</li>' for item in ingredients]),
         | 
| 311 | 
             
                                        "</ul>",
         | 
| 312 | 
            +
                                        "<h3 class='directions font-body text-bold'>Directions</h3>",
         | 
| 313 | 
            +
                                        "<ol class='ingredients-list font-body'>",
         | 
| 314 | 
             
                                        " ".join([f'<li>{item}</li>' for item in directions]),
         | 
| 315 | 
             
                                        "</ol>",
         | 
| 316 | 
             
                                        "</div>"
         | 
    	
        asset/css/style.css
    CHANGED
    
    | @@ -2,6 +2,18 @@ body { | |
| 2 | 
             
                background-color: #fff;
         | 
| 3 | 
             
            }
         | 
| 4 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 5 |  | 
| 6 | 
             
            .fullScreenFrame > div {
         | 
| 7 | 
             
                display: flex;
         | 
| @@ -29,7 +41,7 @@ body { | |
| 29 | 
             
            }
         | 
| 30 | 
             
            .contributors a.contributor {
         | 
| 31 | 
             
                text-decoration: none;
         | 
| 32 | 
            -
                color: # | 
| 33 | 
             
            }
         | 
| 34 | 
             
            .contributors a.contributor:hover {
         | 
| 35 | 
             
                text-decoration: underline;
         | 
| @@ -37,17 +49,24 @@ body { | |
| 37 |  | 
| 38 | 
             
            .story-box {
         | 
| 39 | 
             
                overflow-y: scroll;
         | 
| 40 | 
            -
                max-height:  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 41 | 
             
            }
         | 
| 42 |  | 
| 43 | 
             
            .r-text-recipe {
         | 
| 44 | 
            -
                padding-left: 30px;
         | 
| 45 | 
            -
                margin-left: 10px | 
| 46 | 
            -
                border- | 
| 47 | 
             
            }
         | 
| 48 |  | 
| 49 | 
             
            .divider {
         | 
| 50 | 
            -
                margin: 5px  | 
| 51 | 
             
                width: 400px;
         | 
| 52 | 
             
                max-width: 100%;
         | 
| 53 | 
             
                position:relative;
         | 
| @@ -61,23 +80,53 @@ body { | |
| 61 | 
             
            .divider-mask:after {
         | 
| 62 | 
             
                content: '';
         | 
| 63 | 
             
                display: block;
         | 
| 64 | 
            -
                margin: 0 auto;
         | 
| 65 | 
             
                width: 170px;
         | 
| 66 | 
             
                height: 0px;
         | 
| 67 | 
             
                border-bottom: 2px solid #e9a726;
         | 
| 68 | 
             
                border-radius: 10px;
         | 
|  | |
| 69 | 
             
            }
         | 
| 70 |  | 
| 71 | 
             
            .r-text-recipe .food-title {
         | 
| 72 | 
            -
                text-align:  | 
| 73 | 
             
            }
         | 
| 74 | 
             
            .r-text-recipe .food-title img {
         | 
| 75 | 
            -
                max-width:  | 
|  | |
|  | |
|  | |
| 76 | 
             
            }
         | 
| 77 | 
             
            .r-text-recipe .food-title h2 {
         | 
| 78 | 
             
            }
         | 
|  | |
| 79 | 
             
            .ingredients-list {
         | 
| 80 | 
             
                columns: 2;
         | 
| 81 | 
             
                -webkit-columns: 2;
         | 
| 82 | 
             
                -moz-columns: 2;
         | 
| 83 | 
             
            }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 2 | 
             
                background-color: #fff;
         | 
| 3 | 
             
            }
         | 
| 4 |  | 
| 5 | 
            +
            .font-title {
         | 
| 6 | 
            +
                font-family: 'Poppins', sans-serif !important;
         | 
| 7 | 
            +
            }
         | 
| 8 | 
            +
            .font-body {
         | 
| 9 | 
            +
                font-family: 'Montserrat', sans-serif !important;
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
            .text-bold {
         | 
| 12 | 
            +
                font-weight: normal !important;
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
            .text-bold {
         | 
| 15 | 
            +
                font-weight: bold !important;
         | 
| 16 | 
            +
            }
         | 
| 17 |  | 
| 18 | 
             
            .fullScreenFrame > div {
         | 
| 19 | 
             
                display: flex;
         | 
|  | |
| 41 | 
             
            }
         | 
| 42 | 
             
            .contributors a.contributor {
         | 
| 43 | 
             
                text-decoration: none;
         | 
| 44 | 
            +
                color: #585858;
         | 
| 45 | 
             
            }
         | 
| 46 | 
             
            .contributors a.contributor:hover {
         | 
| 47 | 
             
                text-decoration: underline;
         | 
|  | |
| 49 |  | 
| 50 | 
             
            .story-box {
         | 
| 51 | 
             
                overflow-y: scroll;
         | 
| 52 | 
            +
                max-height: 240px;
         | 
| 53 | 
            +
            }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            .story-box p {
         | 
| 56 | 
            +
                font-size: 0.85rem;
         | 
| 57 | 
            +
            }
         | 
| 58 | 
            +
            .story-box pre {
         | 
| 59 | 
            +
                font-size: 0.6rem;
         | 
| 60 | 
             
            }
         | 
| 61 |  | 
| 62 | 
             
            .r-text-recipe {
         | 
| 63 | 
            +
                /* padding-left: 30px;
         | 
| 64 | 
            +
                margin-left: 10px;*/
         | 
| 65 | 
            +
                border-right: 1px dashed #eee;
         | 
| 66 | 
             
            }
         | 
| 67 |  | 
| 68 | 
             
            .divider {
         | 
| 69 | 
            +
                margin: 5px 0;
         | 
| 70 | 
             
                width: 400px;
         | 
| 71 | 
             
                max-width: 100%;
         | 
| 72 | 
             
                position:relative;
         | 
|  | |
| 80 | 
             
            .divider-mask:after {
         | 
| 81 | 
             
                content: '';
         | 
| 82 | 
             
                display: block;
         | 
|  | |
| 83 | 
             
                width: 170px;
         | 
| 84 | 
             
                height: 0px;
         | 
| 85 | 
             
                border-bottom: 2px solid #e9a726;
         | 
| 86 | 
             
                border-radius: 10px;
         | 
| 87 | 
            +
                left: 0px;
         | 
| 88 | 
             
            }
         | 
| 89 |  | 
| 90 | 
             
            .r-text-recipe .food-title {
         | 
| 91 | 
            +
                text-align: left;
         | 
| 92 | 
             
            }
         | 
| 93 | 
             
            .r-text-recipe .food-title img {
         | 
| 94 | 
            +
                max-width: 300px;
         | 
| 95 | 
            +
                float: left;
         | 
| 96 | 
            +
                margin-right: 30px;
         | 
| 97 | 
            +
                margin-bottom: 30px;
         | 
| 98 | 
             
            }
         | 
| 99 | 
             
            .r-text-recipe .food-title h2 {
         | 
| 100 | 
             
            }
         | 
| 101 | 
            +
            .ingredients {}
         | 
| 102 | 
             
            .ingredients-list {
         | 
| 103 | 
             
                columns: 2;
         | 
| 104 | 
             
                -webkit-columns: 2;
         | 
| 105 | 
             
                -moz-columns: 2;
         | 
| 106 | 
             
            }
         | 
| 107 | 
            +
            .directions {
         | 
| 108 | 
            +
                clear: both;
         | 
| 109 | 
            +
                float: none;
         | 
| 110 | 
            +
                padding-top: 20px;
         | 
| 111 | 
            +
                display: block;
         | 
| 112 | 
            +
            }
         | 
| 113 | 
            +
            .directions-list {}
         | 
| 114 | 
            +
             | 
| 115 | 
            +
             | 
| 116 | 
            +
            @media only screen and (max-width: 600px) {
         | 
| 117 | 
            +
                .r-text-recipe {
         | 
| 118 | 
            +
                    border-right: 0;
         | 
| 119 | 
            +
                    border-bottom: 1px dashed #eee;
         | 
| 120 | 
            +
                }
         | 
| 121 | 
            +
                .r-text-recipe .food-title img {
         | 
| 122 | 
            +
                    max-width: 200px;
         | 
| 123 | 
            +
                }
         | 
| 124 | 
            +
                .directions {
         | 
| 125 | 
            +
                    padding-top: 0px;
         | 
| 126 | 
            +
                }
         | 
| 127 | 
            +
                .ingredients-list {
         | 
| 128 | 
            +
                    columns: 1;
         | 
| 129 | 
            +
                    -webkit-columns: 1;
         | 
| 130 | 
            +
                    -moz-columns: 1;
         | 
| 131 | 
            +
                }
         | 
| 132 | 
            +
            }
         | 
    	
        meta.py
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 | 
             
            HEADER_INFO = """""".strip()
         | 
| 2 | 
             
            SIDEBAR_INFO = """
         | 
| 3 | 
            -
            <div class="contributors">
         | 
| 4 | 
             
            <a class="contributor comma" href="https://huggingface.co/m3hrdadfi">Mehrdad Farahani</a>
         | 
| 5 | 
             
            <a class="contributor comma" href="https://huggingface.co/dk-crazydiv">Kartik Godawat</a>
         | 
| 6 | 
             
            <a class="contributor comma" href="https://huggingface.co/hassiahk">Haswanth Aekula</a>
         | 
| @@ -9,13 +9,15 @@ SIDEBAR_INFO = """ | |
| 9 | 
             
            </div>
         | 
| 10 | 
             
            """
         | 
| 11 | 
             
            CHEF_INFO = """
         | 
| 12 | 
            -
            < | 
|  | |
| 13 | 
             
            <span class="d-block extra-info">(We are at your service with two of the best chefs in the world: Chef Scheherazade, 
         | 
| 14 | 
            -
            Chef Giovanni. Scheherazade is known for being more creative whereas Giovanni is more meticulous.)</span | 
|  | |
| 15 | 
             
            """.strip()
         | 
| 16 | 
             
            PROMPT_BOX = "Add custom ingredients here (separated by `,`): "
         | 
| 17 | 
             
            STORY = """
         | 
| 18 | 
            -
            <div class="story-box">
         | 
| 19 | 
             
            <p>
         | 
| 20 | 
             
            Hello everyone 👋, I am <strong>Chef Transformer</strong>, 
         | 
| 21 | 
             
            the owner of this restaurant. I was made by a group of <a href="https://huggingface.co/flax-community/t5-recipe-generation#team-members">NLP Engineers</a> to train my two prodigy recipe creators: <strong>Chef Scheherazade</strong> and <strong>Chef Giovanni</strong>. 
         | 
| @@ -28,8 +30,7 @@ The NLP engineers helped guide the learning process so that the chefs could actu | |
| 28 | 
             
            I trained my chefs by asking them to generate a title, a list of ingredients (including amounts!), and a list of directions after giving them just a simple list of food items. 
         | 
| 29 | 
             
            </p>
         | 
| 30 |  | 
| 31 | 
            -
            <pre>
         | 
| 32 | 
            -
            [Input]
         | 
| 33 | 
             
                {food items*: separated by comma}
         | 
| 34 |  | 
| 35 | 
             
            [Targets]
         | 
|  | |
| 1 | 
             
            HEADER_INFO = """""".strip()
         | 
| 2 | 
             
            SIDEBAR_INFO = """
         | 
| 3 | 
            +
            <div class="contributors font-body text-bold">
         | 
| 4 | 
             
            <a class="contributor comma" href="https://huggingface.co/m3hrdadfi">Mehrdad Farahani</a>
         | 
| 5 | 
             
            <a class="contributor comma" href="https://huggingface.co/dk-crazydiv">Kartik Godawat</a>
         | 
| 6 | 
             
            <a class="contributor comma" href="https://huggingface.co/hassiahk">Haswanth Aekula</a>
         | 
|  | |
| 9 | 
             
            </div>
         | 
| 10 | 
             
            """
         | 
| 11 | 
             
            CHEF_INFO = """
         | 
| 12 | 
            +
            <h2 class="font-title">Welcome to our lovely restaurant! </h2>
         | 
| 13 | 
            +
            <p class="strong font-body">
         | 
| 14 | 
             
            <span class="d-block extra-info">(We are at your service with two of the best chefs in the world: Chef Scheherazade, 
         | 
| 15 | 
            +
            Chef Giovanni. Scheherazade is known for being more creative whereas Giovanni is more meticulous.)</span>
         | 
| 16 | 
            +
            </p>
         | 
| 17 | 
             
            """.strip()
         | 
| 18 | 
             
            PROMPT_BOX = "Add custom ingredients here (separated by `,`): "
         | 
| 19 | 
             
            STORY = """
         | 
| 20 | 
            +
            <div class="story-box font-body">
         | 
| 21 | 
             
            <p>
         | 
| 22 | 
             
            Hello everyone 👋, I am <strong>Chef Transformer</strong>, 
         | 
| 23 | 
             
            the owner of this restaurant. I was made by a group of <a href="https://huggingface.co/flax-community/t5-recipe-generation#team-members">NLP Engineers</a> to train my two prodigy recipe creators: <strong>Chef Scheherazade</strong> and <strong>Chef Giovanni</strong>. 
         | 
|  | |
| 30 | 
             
            I trained my chefs by asking them to generate a title, a list of ingredients (including amounts!), and a list of directions after giving them just a simple list of food items. 
         | 
| 31 | 
             
            </p>
         | 
| 32 |  | 
| 33 | 
            +
            <pre>[Inputs]
         | 
|  | |
| 34 | 
             
                {food items*: separated by comma}
         | 
| 35 |  | 
| 36 | 
             
            [Targets]
         | 
    	
        Build Ingredients Vocab.ipynb → notes/Build Ingredients Vocab.ipynb
    RENAMED
    
    | 
            File without changes
         | 
    	
        utils/__init__.py
    ADDED
    
    | 
            File without changes
         | 
    	
        utils/api.py
    ADDED
    
    | @@ -0,0 +1,26 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import random
         | 
| 2 | 
            +
            import requests
         | 
| 3 | 
            +
             | 
| 4 | 
            +
             | 
| 5 | 
            +
            def generate_cook_image(query, app_id, app_key):
         | 
| 6 | 
            +
                api_url = f"https://api.edamam.com/api/recipes/v2?type=public&q={query}&app_id={app_id}&app_key={app_key}&field=image"
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                try:
         | 
| 9 | 
            +
                    r = requests.get(api_url)
         | 
| 10 | 
            +
                    if r.status_code != 200:
         | 
| 11 | 
            +
                        return None
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    rj = r.json()
         | 
| 14 | 
            +
                    if "hits" not in rj or not len(rj["hits"]) > 0:
         | 
| 15 | 
            +
                        return None
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    data = rj["hits"]
         | 
| 18 | 
            +
                    data = data[random.randint(1, min(5, len(data) - 1))] if len(data) > 1 else data[0]
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                    if "recipe" not in data or "image" not in data["recipe"]:
         | 
| 21 | 
            +
                        return None
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    image = data["recipe"]["image"]
         | 
| 24 | 
            +
                    return image
         | 
| 25 | 
            +
                except Exception as e:
         | 
| 26 | 
            +
                    return None
         | 
    	
        utils/draw.py
    ADDED
    
    | @@ -0,0 +1,86 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            from PIL import (
         | 
| 2 | 
            +
                Image,
         | 
| 3 | 
            +
                ImageFont,
         | 
| 4 | 
            +
                ImageDraw
         | 
| 5 | 
            +
            )
         | 
| 6 | 
            +
            import textwrap
         | 
| 7 | 
            +
            from .utils import load_image_from_url
         | 
| 8 | 
            +
            from .ext import (
         | 
| 9 | 
            +
                ingredients as ext_ingredients,
         | 
| 10 | 
            +
                directions as ext_directions
         | 
| 11 | 
            +
            )
         | 
| 12 | 
            +
             | 
| 13 | 
            +
             | 
| 14 | 
            +
            def generate_food_with_logo_image(bg_path, logo_path, food_url, no_food="asset/frame/no_food.png"):
         | 
| 15 | 
            +
                bg = Image.open(bg_path)
         | 
| 16 | 
            +
                width, height = bg.size
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                logo = Image.open(logo_path)
         | 
| 19 | 
            +
                logo_width, logo_height, logo_ratio, logo_rb, logo_mb = logo.size + (3, -20, 45)
         | 
| 20 | 
            +
                logo_width, logo_height = (logo_width // logo_ratio, logo_height // logo_ratio)
         | 
| 21 | 
            +
                logo = logo.resize((logo_width, logo_height))
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                food = load_image_from_url(food_url, rgba_mode=True, default_image=no_food)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                food_width, food_height = (300, 300)
         | 
| 26 | 
            +
                food = food.resize((food_width, food_height))
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                bg.paste(food, (0, 0), food)
         | 
| 29 | 
            +
                bg.paste(logo, (width - logo_width - logo_rb, height - logo_height - logo_mb), logo)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                return bg
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 34 | 
            +
            def generate_recipe_image(
         | 
| 35 | 
            +
                    recipe_data,
         | 
| 36 | 
            +
                    bg_path,
         | 
| 37 | 
            +
                    food_logo_ia,
         | 
| 38 | 
            +
                    fonts,
         | 
| 39 | 
            +
                    bg_color="#ffffff"
         | 
| 40 | 
            +
            ):
         | 
| 41 | 
            +
                bg = Image.open(bg_path)
         | 
| 42 | 
            +
                bg.paste(food_logo_ia, (50, 50), food_logo_ia)
         | 
| 43 | 
            +
                bg_color = Image.new("RGBA", bg.size, bg_color)
         | 
| 44 | 
            +
                bg_color.paste(bg, mask=bg)
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                im_editable = ImageDraw.Draw(bg_color)
         | 
| 47 | 
            +
                im_editable.text(
         | 
| 48 | 
            +
                    (418, 30),
         | 
| 49 | 
            +
                    textwrap.fill(recipe_data["title"], 15).replace(" \n", "\n"),
         | 
| 50 | 
            +
                    (61, 61, 70),
         | 
| 51 | 
            +
                    font=fonts["title"],
         | 
| 52 | 
            +
                )
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                im_editable.text(
         | 
| 55 | 
            +
                    (100, 450),
         | 
| 56 | 
            +
                    "Ingredients",
         | 
| 57 | 
            +
                    (61, 61, 70),
         | 
| 58 | 
            +
                    font=fonts["body_bold"],
         | 
| 59 | 
            +
                )
         | 
| 60 | 
            +
                ingredients = recipe_data["ingredients"]
         | 
| 61 | 
            +
                ingredients = [textwrap.fill(item, 30).replace("\n", "\n   ") for item in ingredients]
         | 
| 62 | 
            +
                ingredients = ext_ingredients(ingredients, [], without_mapping=True)
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                im_editable.text(
         | 
| 65 | 
            +
                    (50, 520),
         | 
| 66 | 
            +
                    "\n".join([f"- {item}" for item in ingredients]),
         | 
| 67 | 
            +
                    (61, 61, 70),
         | 
| 68 | 
            +
                    font=fonts["body"],
         | 
| 69 | 
            +
                )
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                im_editable.text(
         | 
| 72 | 
            +
                    (700, 450),
         | 
| 73 | 
            +
                    "Directions",
         | 
| 74 | 
            +
                    (61, 61, 70),
         | 
| 75 | 
            +
                    font=fonts["body_bold"],
         | 
| 76 | 
            +
                )
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                directions = recipe_data["directions"]
         | 
| 79 | 
            +
                directions = [textwrap.fill(item, 70).replace("\n", "\n   ").capitalize() for item in directions]
         | 
| 80 | 
            +
                im_editable.text(
         | 
| 81 | 
            +
                    (430, 520),
         | 
| 82 | 
            +
                    "\n".join([f"{i + 1}. {item}" for i, item in enumerate(directions)]).strip(),
         | 
| 83 | 
            +
                    (61, 61, 70),
         | 
| 84 | 
            +
                    font=fonts["body"],
         | 
| 85 | 
            +
                )
         | 
| 86 | 
            +
                return bg_color
         | 
    	
        utils/ext.py
    ADDED
    
    | @@ -0,0 +1,43 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import re
         | 
| 2 | 
            +
            from .utils import replace_regex
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            DEFAULT_MAP_DICT = {
         | 
| 5 | 
            +
                " c ": " c. ",
         | 
| 6 | 
            +
                ", chopped": " (chopped)",
         | 
| 7 | 
            +
                ", crumbled": " (crumbled)",
         | 
| 8 | 
            +
                ", thawed": " (thawed)",
         | 
| 9 | 
            +
                ", melted": " (melted)",
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
            def ingredient(text, map_dict):
         | 
| 14 | 
            +
                if len(map_dict) > 0:
         | 
| 15 | 
            +
                    map_dict.update(**DEFAULT_MAP_DICT)
         | 
| 16 | 
            +
                else:
         | 
| 17 | 
            +
                    map_dict = DEFAULT_MAP_DICT
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                text = replace_regex(text, map_dict)
         | 
| 20 | 
            +
                text = re.sub(r"(\d)\s(\d\/\d)", r" \1+\2 ", text)
         | 
| 21 | 
            +
                text = " ".join([word.strip() for word in text.split() if word.strip()])
         | 
| 22 | 
            +
                return text
         | 
| 23 | 
            +
             | 
| 24 | 
            +
             | 
| 25 | 
            +
            def ingredients(text_list, item_list, without_mapping=False):
         | 
| 26 | 
            +
                map_dict = {
         | 
| 27 | 
            +
                    item: f'<span class="text-bold">{item}</span>' for item in list(map(lambda x: x.lower().strip(), item_list))
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
                text_list = list(map(lambda x: x.lower(), text_list))
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                output = []
         | 
| 32 | 
            +
                for text in text_list:
         | 
| 33 | 
            +
                    map_dict = map_dict if not without_mapping else {}
         | 
| 34 | 
            +
                    text = ingredient(text, map_dict)
         | 
| 35 | 
            +
                    output.append(text)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                return output
         | 
| 38 | 
            +
             | 
| 39 | 
            +
             | 
| 40 | 
            +
            def directions(text_list):
         | 
| 41 | 
            +
                text_list = list(map(lambda x: x.lower().capitalize(), text_list))
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                return text_list
         | 
    	
        utils/st.py
    ADDED
    
    | @@ -0,0 +1,10 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import streamlit as st
         | 
| 2 | 
            +
             | 
| 3 | 
            +
             | 
| 4 | 
            +
            def local_css(css_path):
         | 
| 5 | 
            +
                with open(css_path) as f:
         | 
| 6 | 
            +
                    st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            def remote_css(css_url):
         | 
| 10 | 
            +
                st.markdown(f'<link href="{css_url}" rel="stylesheet">', unsafe_allow_html=True)
         | 
    	
        utils/utils.py
    ADDED
    
    | @@ -0,0 +1,73 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            import base64
         | 
| 2 | 
            +
            import json
         | 
| 3 | 
            +
            from io import BytesIO
         | 
| 4 | 
            +
            from PIL import Image
         | 
| 5 | 
            +
            import requests
         | 
| 6 | 
            +
            import re
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            def load_image_from_local(image_path, image_resize=None):
         | 
| 10 | 
            +
                image = Image.open(image_path)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                if isinstance(image_resize, tuple):
         | 
| 13 | 
            +
                    image = image.resize(image_resize)
         | 
| 14 | 
            +
                return image
         | 
| 15 | 
            +
             | 
| 16 | 
            +
             | 
| 17 | 
            +
            def load_image_from_url(image_url, rgba_mode=False, image_resize=None, default_image=None):
         | 
| 18 | 
            +
                try:
         | 
| 19 | 
            +
                    image = Image.open(requests.get(image_url, stream=True).raw)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    if rgba_mode:
         | 
| 22 | 
            +
                        image = image.convert("RGBA")
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    if isinstance(image_resize, tuple):
         | 
| 25 | 
            +
                        image = image.resize(image_resize)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                except Exception as e:
         | 
| 28 | 
            +
                    image = None
         | 
| 29 | 
            +
                    if default_image:
         | 
| 30 | 
            +
                        image = load_image_from_local(default_image, image_resize=image_resize)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                return image
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 35 | 
            +
            def load_text(text_path):
         | 
| 36 | 
            +
                text = ''
         | 
| 37 | 
            +
                with open(text_path) as f:
         | 
| 38 | 
            +
                    text = f.read()
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                return text
         | 
| 41 | 
            +
             | 
| 42 | 
            +
             | 
| 43 | 
            +
            def load_json(json_path):
         | 
| 44 | 
            +
                jdata = ''
         | 
| 45 | 
            +
                with open(json_path) as f:
         | 
| 46 | 
            +
                    jdata = json.load(f)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                return jdata
         | 
| 49 | 
            +
             | 
| 50 | 
            +
             | 
| 51 | 
            +
            def image_to_base64(image_array):
         | 
| 52 | 
            +
                buffered = BytesIO()
         | 
| 53 | 
            +
                image_array.save(buffered, format="PNG")
         | 
| 54 | 
            +
                image_b64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
         | 
| 55 | 
            +
                return f"data:image/png;base64, {image_b64}"
         | 
| 56 | 
            +
             | 
| 57 | 
            +
             | 
| 58 | 
            +
            def unique_list(seq):
         | 
| 59 | 
            +
                seen = set()
         | 
| 60 | 
            +
                seen_add = seen.add
         | 
| 61 | 
            +
                return [x for x in seq if not (x in seen or seen_add(x))]
         | 
| 62 | 
            +
             | 
| 63 | 
            +
             | 
| 64 | 
            +
            def pure_comma_separation(list_str, return_list=True):
         | 
| 65 | 
            +
                r = unique_list([item.strip() for item in list_str.lower().split(",") if item.strip()])
         | 
| 66 | 
            +
                if return_list:
         | 
| 67 | 
            +
                    return r
         | 
| 68 | 
            +
                return ", ".join(r)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
             | 
| 71 | 
            +
            def replace_regex(text, map_dict):
         | 
| 72 | 
            +
                pattern = "|".join(map(re.escape, map_dict.keys()))
         | 
| 73 | 
            +
                return re.sub(pattern, lambda m: map_dict[m.group()], str(text))
         | 

