Skip to content

Instantly share code, notes, and snippets.

@cvpe
Created October 10, 2020 19:44
Show Gist options
  • Save cvpe/eb667c6256f150b3dbd115ce986c8767 to your computer and use it in GitHub Desktop.
Save cvpe/eb667c6256f150b3dbd115ce986c8767 to your computer and use it in GitHub Desktop.
WeightWatchers.py
'''
todo
.. Priorité
ok - record Aliment points_base et base et option ingredients
.. - 1️⃣ revoir add_food
.. - ❗️pas créer aliment si nom existe déjà
check dans ecran où on tape le nom et le ok
.. - ecrans où on doit checker
.. - nom: pas vide, inexistant
.. - kcal: vide? ou numerique
.. - ...
ok - 🆗 aliment type base
ok - call add_food
ok - test et check si dans liste aliments
ok - appel direct à use_food
ok - use dans repas
ok - 🆗 aliment à ingrédients
ok - call add_food
ok - test et check si dans liste aliments
ok - appel direct à use_food
ok - use dans repas
.. - 2️⃣ aliment à barcode
.. - call add_food
.. - test et check si dans liste aliments
.. - appel direct à use_food
.. - use dans repas
.. - 3️⃣ select recette => ecran usage special multi aliments/quantités
ok - nom/pts par unité/qté textfield points calculés
.. - 🛑crash at ok.....
memo tf dans cc.values du textfield du dialog
.. - utiliser recette en tout (melange) ou par element
aussi bien en creation (fields total gr, pts) qu'en usage'
ex: galettes = ingredients mais total 860 gr = 225 points
usage: 25 gr => calcul points
.. - add elements recette to meal
.. - 4️⃣ creer recette, comme un repas en y ajoutant free ou aliment
.. 🛑 bugs
.. - search in db record does not work
.. ➖ bonus?
.. - ajouter 'groupe' aux aliments ex: boissons => sections
.. ➖ technique
.. - py et db sur icloud
.. - tests sur iphone
.. ➖ db update
.. - enlever le recreate db de chaque run
ok - technical button
ok - tableview
ok - section = rec_type
ok - row = rec_key
ok - sort sur key: param/user/poids/points/recettes/aliments
ok - textfield de recherche
.. - 0️⃣select => ui.View
.. - textfield rec_key
.. - textfield rec_subkey
.. - textview rec_data
.. - ou tableview si array de dict comme les repas?
.. - boutons add/delete/update
.. - update aliment => montre detail, meme ingredients
.. ➖ fichier départ
ok - user
ok - poids
ok - points
ok - aliments ean, titre, ingredients, unite=100gr, type portion revoir
.. ex: ean seul, le reste sera automatique
.. ex: pommes de terre,...
.. - ean seul => web, genere record aliment avec thumb
.. - repas?
.. ➖ boissons via menu particulier ou parmi aliments?
ok ➖ 🆗 ecran principal
ok - ui objets dans ui.view, pas dialog,
ok - semaine en cours, current, jours faits, jour a faire
ex: Ve 23 et en dessous points du jour
couleur vert/rouge suivant points si fait, bg si today
ok - bouton date => change jour
ok - alors boutons < et > pour semaine precedente et suivante
ok - si aucun jour dans db, tout gris et vide
ok - date de today au debut
ok - call set_day
ok - créer record Points si existe pas (voir plus bas)
ok - boutons jour prec, suivant, max aujourd'hui
ok - calcul compteurs de points et les afficher
ok - grille des points jour/hebdo
ok - pour demain doit etre 0 si dimanche
ok - points utilises ce jour *** rouge si > 23
ok - tableview sections=petitdej, dej, souper, collations
ok - header: points chaque repas
ok - TableView sans header et footer
ok - row repas, deux labels
ok - row
ok - soit aliment
ok subtitle: nn points 30 gr de xxxxxxx
ok - utiliser f"" format
ok - soit recette dont on voit les aliments
ok - db record: ajoute option 'recette'
ok - affichage en 2eme ligne "ingrédient de la recette xxx"
ok - db record
Points|date|reserve hebdo matin|[aliments,...]
les points consommés sont dans les aliments, donc on peut
afficher: consommés,reserve hebdo restante, points restants
aliment = {'repas':1, aliment':nom, 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')}
ok - did_select aliment
ok - no action si row de repas
ok - --> ecran usage
ok - init segment repas où se trouve cet aliment
ok - delete
ok - ask confirmation
ok - change repas
ok - save record
ok - aliment zero point: pas de quantité et special pour 0 points
.. ➖ paramètres
ok - bouton
.. - db record 'Param', , ,{'units':(gr,cl,cs,...)}
.. - tableview
.. - unités
.. - créer liste qu'on utilisera dans segments ou rollpicker
.. - gr
.. - cl
.. - ml
.. - cuillère à soupe (cs) = 15 gr/cl
.. - cuillère à café (cc) = 5 gr/cl
.. - pièce
.. - noms des repas
ok ➖ user
ok - bouton
ok - db record
ok - dialog infos
ok - record db
.. - add
.. - update
ok ➖ 🆗 poids
ok - bouton aujourd'hui -> ajoute, init dateroller à aujourd'hui
ok - db record
ok - graphique
ok - légende
ok - support 0 record
ok - graphique 1 pt = x
ok - bug: trier dates des poids
ok - select, puis comme add_button
ok - comment faire delete? switch?
ok - confirm
ok - add => refresh liste et graphique (retour et recall dialog)
ok - check poids introduit existe et numerique
ok - checks dans action dans my_form_dialog
ok - add: check new date n'existe pas
ok - upd: si date et poids inchangés, alert et aucune action
ok - upd: check new date n'existe pas
ok - actions sur db dans main view
ok - add: add record
ok - del: del record
ok - upd: upd record meme si date change
ok ➖ 🆗 evolution points
ok - bouton calendrier
.. - graphique optionnel?
ok - tableview non editable
ok - support 0 record
ok - graphique 1 pt = x
ok - points et param 23, pas reserve
.. - bouton detail? => liste complete des aliments
.. - use un de ces aliments dans repas d'aujourd'hui (ecran usage)
ok - graphique
ok - légendes
ok - points du jour
ok - points reserve utilises
ok - horizontale
ok - select => set date de ecran principal
.. ➖ 1️⃣ ecran usage d'un aliment
.. - via def avec parametres tout ce qui est necessaire
.. - titre en parametre
ok - pour aliment zero point
ok - pour aliment existant selecté
.. - pour aliment calculatrice
.. - pour aliment via barcode
ok - pour aliment d'un repas existant selecté
.. - quantité
.. - roller
.. - check numerique si via textfield
ok - qui devrait changer les points directement dans le dialog
.. - choix poids ou portion
.. - sauf si zero points
ok - calcul et affichage des points
ok - choix pour quel repas (segment?)
ok - repas en cours par defaut
ok - si select segment, set repas en cours
ok ➖ 🆗 liste aliments zero points
ok - bouton
ok - db record
ok - tableview
ok - select => usage
ok - add aliment to day
ok ➖ 🆗 liste aliments (non zero)
ok - bouton
ok - db record
'Aliment', nom, barcode,
ex: {'points':12, 'base':(100,'gr'), 'kcal':359,'saturated_fat':0.5,'sugars':1.5,'proteins':12.5, 'thumb':None/data, 'portion':('pièce',7,'gr'), 'groupe':'féculents'}
ok - tableview thumb nom
ok - sort sur nom
ok - row: nom / 12 points pour 100 gr / thumb
ok - mettre subtitle à la fin de la row
ok - row moins large
ok - use f format
ok - select => usage => add to day meal
.. ➖ def add aliment dans db
.. - revoir parametres => record dictionnary
.. - si existe: demander si update, ou changement nom
.. - attention pas nom de aliment zeropoint!!!!!!!
.. - oui => update db
.. - ❗️si changement points, scan passé check si deja utilise dans
repas ou jour
.. - non => retour dans ecran appelant
ok - si existe pas: add dans db
.. ➖ ingredients
ok - bouton
ok - dialog ingredients + nom donné?
.. - ❗️pour un poids/volume/... choix base
ok - smartpoints calculé pendant frappe
.. - check numerique et autre
ok - add dans db
.. - select => usage
.. ➖ scan
ok - bouton
.. - possibilite taper barcode
ok - scan barcode scanner.py
ok - get infos du web....
ok - display titre, image, infos graisses...., bouton voir page detail
ok - webview
ok - poids une portion, ex: 1 speculoos
ok - points une portion
.. - memo db ean/titre/ingredients/unit/100/thumb? pour list view
.. ➖ liste de recettes connus
ok - bouton
ok - db record
'recette' nom '' [{'aliment':'xxxx','quantité':20,'unit':'gr','points':5},...
ok - tableview
ok - tri
ok - recherche
ok - select => usage, modifiable par exemple, un peu plus de fromage
ok - meme que usage un seul aliment? mais plusieurs lignes?
.. - check numerique
.. - comment créer un recette? à partir d'un repas?
.. - dans ecran principal, bouton 'creation recette'
.. - puis des check apparaissent au lieu des points sur aliments
.. - puis sorte d'ecran usage où il faut choisir nom + rollers qté
.. - ❗️ comme ajouter aliment a day mais ici a recette
.. - quid si aliment zeropoint dans recette, pas memes zones?
ok ➖ 🆗 search:
ok - locate at first
ok - boutons next, previous
ok ➖ 🆗 at close, backup db local sur icloud drive
'''
import ast
import collections
import console
from ctypes import c_void_p
from datetime import datetime, timedelta
import dialogs
from functools import partial
import importlib
import io
import json
from objc_util import *
import os
from PIL import Image
import requests
import shutil
import sqlite3
import swizzle
import sys
import ui
import unicodedata
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib
import numpy as np
from io import BytesIO
PY3 = sys.version_info[0] >= 3
if PY3:
basestring = str
NSLocale = ObjCClass('NSLocale')
fr_locale = NSLocale.alloc().initWithLocaleIdentifier_(ns('fr_FR'))
# needed for barcode scan: begin
AVCaptureSession = ObjCClass('AVCaptureSession')
AVCaptureDevice = ObjCClass('AVCaptureDevice')
AVCaptureDeviceInput = ObjCClass('AVCaptureDeviceInput')
AVCaptureMetadataOutput = ObjCClass('AVCaptureMetadataOutput')
AVCaptureVideoPreviewLayer = ObjCClass('AVCaptureVideoPreviewLayer')
dispatch_get_current_queue = c.dispatch_get_current_queue
dispatch_get_current_queue.restype = c_void_p
def captureOutput_didOutputMetadataObjects_fromConnection_(_self, _cmd, _output, _metadata_objects, _conn):
global MainView
objects = ObjCInstance(_metadata_objects)
for obj in objects:
s = str(obj.stringValue())
if s != MainView.barcode:
MainView.barcode = s
#print(s)
MainView.scan_view.close()
MetadataDelegate = create_objc_class('MetadataDelegate', methods=[captureOutput_didOutputMetadataObjects_fromConnection_], protocols=['AVCaptureMetadataOutputObjectsDelegate'])
# needed for barcode scan: end
def my_show_datepicker(self,mode):
original_show_datepicker(self,mode)
date_picker_pntr = ObjCInstance(self.date_picker)
date_picker_pntr.setLocale_(fr_locale)
importlib.reload(dialogs) # to avoid infinite recursion
original_show_datepicker = dialogs._FormDialogController.show_datepicker
dialogs._FormDialogController.show_datepicker = my_show_datepicker
def my_tableview_did_select(tv, section, row):
global cc
#print(section, row)
#print(cc.container_view.name)
if 'ajoute' in cc.container_view.name:
pass
elif 'la db' in cc.container_view.name:
# selection of a db record
cc.select = (section, row)
cc.container_view.close()
return
elif 'zero' in cc.container_view.name:
# selection of a zero point aliment
cc.select = (section, row)
cc.container_view.close()
return
elif 'aliments' in cc.container_view.name:
# selection of existing aliment
cc.select = (section, row)
cc.container_view.close()
return
elif 'poids' in cc.container_view.name:
ui.delay(poids_button_action,0.1)
return
elif 'Calendrier' in cc.container_view.name:
cc.select = (section, row)
cc.container_view.close()
return
elif 'recettes' in cc.container_view.name:
cc.select = (section, row)
cc.container_view.close()
return
cc.original_tableview_did_select(tv, section, row)
def my_tableview_cell_for_row(tableview, section, row):
global cc
if cc.container_view.name == 'Liste des aliments':
orig_cell = cc.cells[section][row]
cell = ui.TableViewCell()
cell.bg_color = 'white'
h = cc.view.row_height
field = cc.sections[section][1][row]
# thumb 5 name 5 subtitle 5
# original row text
t = orig_cell.text_label.text
l1 = ui.Label(name='l1')
l1.text_color = orig_cell.text_label.text_color
l1.font = ('Menlo',16)
l1.text = t
w = (tableview.width - h - 3*5)*3/4
l1.frame = (h+5,0,w,h)
cell.content_view.add_subview(l1)
# eventual row subtitle
if 'subtitle' in field:
l2 = ui.Label()
l2.text_color = 'blue'
l2.font = ('Menlo',12)
l2.text = field['subtitle']
x = l1.x + l1.width + 5
w = tableview.width - x - 5
l2.frame = (x,0,w,h)
cell.content_view.add_subview(l2)
# eventual thumb
if orig_cell.image_view.image:
thumb = ui.ImageView()
thumb.frame = (tableview.width-h,0,h,h)
thumb.image = orig_cell.image_view.image
thumb.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
cell.content_view.add_subview(thumb)
return cell
elif 'Ajoute recette' in cc.container_view.name:
if 'Ingrédients' not in cc.sections[section][0]:
return cc.cells[section][row]
cell = ui.TableViewCell()
cell.bg_color = 'white'
field = cc.sections[section][1][row]
# viande 200 gr --> 4 points
# 2 points / 100 gr
# l1 et l2 tf l3 l4 l5 l6
# subtitle
h = cc.view.row_height
h1 = h*2/3
l5 = ui.Label(name='l5')
l5.frame = (tableview.width-5-80,0,80,h)
l5.font = ('Menlo',12)
l5.text = str(field['points']) + ' points'
l5.alignment = ui.ALIGN_RIGHT
cell.content_view.add_subview(l5)
l4 = ui.Label()
l4.frame = (l5.x-30,0,30,h)
l4.font = ('Menlo',12)
l4.text = '-->'
cell.content_view.add_subview(l4)
l3 = ui.Label(name='l3')
l3.text_color = 'blue'
l3.font = ('Menlo',12)
l3.text = field['units']
l3.frame = (l4.x-40,0,40,h)
cell.content_view.add_subview(l3)
tf = ui.TextField(name='ingredient_of_recette')
tf.font = ('Menlo',16)
tf.alignment = ui.ALIGN_RIGHT
tf.frame = (l3.x-100-5,(h-20)/2,100,20)
tf.text = str(field['quantity'])
tf.delegate = c
tf.l5 = l5
tf.values = field['key']
tf.points_per_unit = field['points_per_unit']
cell.content_view.add_subview(tf)
t = field['title']
l1 = ui.Label(name='l1')
l1.text_color = 'black'
l1.font = ('Menlo',16)
l1.text = t
l1.frame = (5,0,tf.x-5,h1)
cell.content_view.add_subview(l1)
l2 = ui.Label()
l2.text_color = 'blue'
l2.font = ('Menlo',10)
l2.text = field['subtitle']
l2.frame = (l1.x,l1.y+l1.height,l1.width,h-h1)
cell.content_view.add_subview(l2)
return cell
else:
return cc.cells[section][row]
def ui2pil(ui_img):
return Image.open(io.BytesIO(ui_img.to_png()))
def pil2ui(imgIn):
with io.BytesIO() as bIO:
imgIn.save(bIO, 'PNG')
imgOut = ui.Image.from_data(bIO.getvalue())
del bIO
return imgOut
def smartpoints(kcal,saturated_fat,sugars,proteins):
# Smart Points Food Value Calculation
# (Calories * .0305) + (Sat Fat * .275) + (Sugar * .12) - (Protein * .098)
# https://www.calculator.net/weight-watchers-points-calculator.html
sp = 0.0305*kcal + 0.275*saturated_fat + 0.12*sugars - 0.098*proteins
return int(sp) # or round(sp)?
def scroll_to(loc):
global cc
section, row = loc
tblo = ObjCInstance(cc.view) # ui.TableView
NSIndexPath = ObjCClass("NSIndexPath")
nsindex = NSIndexPath.indexPathForRow_inSection_(row,section)
UITableViewScrollPositionTop = 1
tblo.scrollToRowAtIndexPath_atScrollPosition_animated_(nsindex, UITableViewScrollPositionTop, True)
def my_textfield_did_change(textfield):
global cc
#print(textfield.name)
if '🔍' in textfield.name:
# field is search field
# sort, searched first, gray other ones, not selectable
txt = textfield.text
txt = unicodedata.normalize('NFKD', txt).encode('ASCII', 'ignore')
txt = str(txt,'utf-8').upper()
cc.search_locations = []
cc.search_locations_current = -1
for s in range(0,len(cc.sections)):
for i in range(0,len(cc.cells[s])): # loop on rows of section s
cell = cc.cells[s][i] # ui.TableViewCell of row i
# cell is only a label, or has a TextField subview
# but if we use cell_for_row, like in 'Liste aliments',
# then an ow label is used instead of cell.text_label
tf = cell.text_label
t = tf.text
t = unicodedata.normalize('NFKD', t).encode('ASCII', 'ignore')
t = str(t,'utf-8').upper()
if txt not in t:
tf.text_color = 'lightgray'
else:
tf.text_color = 'black'
cc.search_locations.append((s,i))
if cc.search_locations_current < 0:
cc.search_locations_current = 0
scroll_to(cc.search_locations[cc.search_locations_current])
cc.view.reload_data()
elif textfield.name == 'ingredient_of_recette':
textfield.l5.text = str(int(float(textfield.text)*textfield.points_per_unit)) + ' points'
cc.values[textfield.values] = textfield.l5.text
elif textfield.name in ['Kcal', 'Graisses saturées', 'Sucre', 'Protéines']:
for s in range(0,len(cc.sections)):
for i in range(0,len(cc.cells[s])): # loop on rows of section 1
cell = cc.cells[s][i] # ui.TableViewCell of row i
if len(cell.content_view.subviews) > 0:
tf = cell.content_view.subviews[0] # ui.TextField of value in row
if type(tf) is ui.TextField:
t = cell.text_label.text
try:
v = float(tf.text)
except:
v = 0
if t == 'Kcal':
kcal = v
elif t == 'Graisses saturées':
saturated_fat = v
elif t == 'Sucre':
sugars = v
elif t == 'Protéines':
proteins = v
elif t == 'Points':
tf.text = str(sp)
tf.text_color = 'red'
cc.original_textfield_did_change(tf)
if 'Ingrédients' in cc.sections[s][0]:
sp = smartpoints(kcal,saturated_fat,sugars,proteins)
elif 'quantité (en' in textfield.name:
for s in range(0,len(cc.sections)):
if 'Points' not in cc.sections[s][0]:
continue
# Section 'Points'
for i in range(0,len(cc.cells[s])): # loop on rows of section 1
cell = cc.cells[s][i] # ui.TableViewCell of row i
if len(cell.content_view.subviews) > 0:
tf = cell.content_view.subviews[0] # ui.TextField of value in row
if type(tf) is ui.TextField:
t = cell.text_label.text.strip() # "12 points par 100 gr"
i = t.find(' ')
np_base = int(t[:i]) # 12
t = t[i:].replace(' points par ','').strip() # 100 gr
i = t.find(' ')
qt_base = int(t[:i]) # 100
np = int(np_base*float('0'+textfield.text)/qt_base)
tf.text = str(np)
tf.text_color = 'red'
cc.original_textfield_did_change(tf)
break
cc.original_textfield_did_change(textfield)
def my_form_dialog(title='', fields=None, sections=None, done_button_title='ok', dims=None, graphique_button=False, graphic_data=None, poids_buttons=False, web_button=False, base_button=False, calc_button=False, scan_button=False, ui_image=False, web_url=None, editable=True, legends=None, ylabel=None, search=None, row_height=None):
global cc, MainView
if not sections and not fields:
raise ValueError('sections or fields are required')
if not sections:
sections = [('', fields)]
if not isinstance(title, basestring):
raise TypeError('title must be a string')
for section in sections:
if not isinstance(section, collections.Sequence):
raise TypeError('Sections must be sequences (title, fields)')
if len(section) < 2:
raise TypeError('Sections must have 2 or 3 items (title, fields[, footer]')
if not isinstance(section[0], basestring):
raise TypeError('Section titles must be strings')
if not isinstance(section[1], collections.Sequence):
raise TypeError('Expected a sequence of field dicts')
for field in section[1]:
if not isinstance(field, dict):
raise TypeError('fields must be dicts')
cc = dialogs._FormDialogController(title, sections, done_button_title=done_button_title)
cc.view.allows_multiple_selection = False
cc.original_tableview_did_select = cc.tableview_did_select
cc.tableview_did_select = my_tableview_did_select
cc.original_textfield_did_change = cc.textfield_did_change
cc.textfield_did_change = my_textfield_did_change
cc.tableview_cell_for_row = my_tableview_cell_for_row
if row_height:
cc.view.row_height = row_height
if dims:
cc.container_view.frame = (0, 0, dims[0],dims[1])
#cc.view.row_height = 32
if search:
w,h = cc.container_view.width, cc.container_view.height
cc.view.frame = (0,50,w,h-50)
cc.view.flex = ''
cc.loupe = ui.Label()
cc.loupe.frame = (0,0,50,50)
cc.loupe.text = ' 🔍 '
cc.loupe.background_color = 'white'
cc.container_view.add_subview(cc.loupe)
cc.search = ui.TextField(name='🔍')
cc.search.placeholder = 'tapez ici un texte à chercher'
cc.search.frame = (50,0,w-150,50)
cc.search.delegate = c
cc.container_view.add_subview(cc.search)
cc.prev_search = ui.Button()
cc.prev_search.background_color = 'white'
cc.prev_search.frame = (cc.search.x+cc.search.width,0,50,50)
cc.prev_search.image = ui.Image.named('iob:ios7_arrow_back_32')
def prev_search_action(sender):
if cc.search_locations_current >= 0:
cc.search_locations_current -= 1
if cc.search_locations_current < 0:
cc.search_locations_current = len(cc.search_locations)-1
scroll_to(cc.search_locations[cc.search_locations_current])
cc.prev_search.action = prev_search_action
cc.container_view.add_subview(cc.prev_search)
cc.next_search = ui.Button()
cc.next_search.background_color = 'white'
cc.next_search.frame = (cc.search.x+cc.search.width+50,0,50,50)
cc.next_search.image = ui.Image.named('iob:ios7_arrow_forward_32')
def next_search_action(sender):
if cc.search_locations_current >= 0:
cc.search_locations_current += 1
if cc.search_locations_current == len(cc.search_locations):
cc.search_locations_current = 0
scroll_to(cc.search_locations[cc.search_locations_current])
cc.next_search.action = next_search_action
cc.container_view.add_subview(cc.next_search)
cc.search_locations_current = -1
if graphique_button:
graphique_button = ui.ButtonItem()
graphique_button.tint_color = 'green'
graphique_button.title = 'graphique'
graphique_button.action = graphique_button_action
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (graphique_button,)
cc.legends = legends
cc.ylabel = ylabel
if poids_buttons:
poids_add_button = ui.ButtonItem()
poids_add_button.tint_color = 'green'
poids_add_button.title = "ajoute"
poids_add_button.action = poids_button_action
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (poids_add_button,)
if web_button:
web_button = ui.ButtonItem()
web_button.tint_color = 'green'
web_button.title = 'détails'
web_button.action = web_button_action
cc.url = web_url
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (web_button,)
if base_button:
base_button = ui.ButtonItem()
base_button.tint_color = 'green'
base_button.title = '🥑'
base_button.action = base_button_action
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (base_button,)
if calc_button:
calc_button = ui.ButtonItem()
calc_button.tint_color = 'green'
calc_button.image = ui.Image.named('iob:ios7_calculator_outline_32').with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
calc_button.action = calc_button_action
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (calc_button,)
if scan_button:
scan_button = ui.ButtonItem()
scan_button.tint_color = 'green'
scan_button.image = ui.Image.from_data(MainView.UIImage_data).with_rendering_mode(ui.RENDERING_MODE_ORIGINAL)
scan_button.action = scan_button_action
# right buttons is a tuple, the way to add an element is "+(element,)"
cc.container_view.right_button_items = cc.container_view.right_button_items + (scan_button,)
for s in range(0,len(cc.sections)):
for i in range(0,len(cc.cells[s])): # loop on rows of section s
cell = cc.cells[s][i] # ui.TableViewCell of row i
#cell.text_label.font = ('Courier',16) # font with 'echappement fixe
# some fields types are subviews of the cell:
# text,number,url,email,password,switch
# but check, date and time are not set as subviews of cell.content_view
if len(cell.content_view.subviews) > 0:
tf = cell.content_view.subviews[0] # ui.TextField of value in row
if type(tf) is ui.TextField:
tf.alignment=ui.ALIGN_RIGHT
if not editable or 'readonly' in sections[s][1][i]:
tf.enabled = False
# attention: tf.name not set for date fields
if 'segments' in sections[s][1][i]:
item = cc.sections[s][1][i] # section s, 1=items, row i
segmented = ui.SegmentedControl()
segmented.name = cell.text_label.text
segmented.frame = tf.frame
segmented.width = 400
segmented.x = cc.view.width - segmented.width - 8
segmented.segments = item['segments']
value = item.get('value', '')
segmented.selected_index = item['segments'].index(value)
cell.content_view.remove_subview(tf)
del cc.values[tf.name]
del tf
cell.content_view.add_subview(segmented)
if ui_image:
cc.view.height = cc.container_view.height*2/3
cc.view.border_width = 2
cc.view.border_color = 'blue'
y = cc.view.y + cc.view.height - 56
w = cc.container_view.width
h = cc.container_view.height - y - 58
x = 0
image_view = ui.ImageView(name='graphic_area', frame=(x,y,w,h))
image_view.background_color = 'lightgray'
image_view.border_width = 2
image_view.border_color ='red'
image_view.content_mode = ui.CONTENT_SCALE_ASPECT_FIT
cc.container_view.add_subview(image_view)
if type(graphic_data) is list:
cc.graphic_data = graphic_data
graphique_button_action('no sender')
else:
image_view.image = ui.Image.from_data(graphic_data)
cc.poids = False
cc.select = False
cc.base = False
cc.calc = False
cc.scan = False
cc.container_view.present('sheet')
cc.container_view.wait_modal()
# Get rid of the view to avoid a retain cycle:
cc.container_view = None
if cc.poids:
return cc.poids
if cc.select:
return cc.select
if cc.base:
return 'base'
if cc.calc:
return 'calc'
if cc.scan:
return 'scan'
if cc.was_canceled:
return None
for s in range(0,len(cc.sections)):
for i in range(0,len(cc.cells[s])): # loop on rows of section 0
cell = cc.cells[s][i] # ui.TableViewCell of row i
# some fields types are subviews of the cell:
# text,number,url,email,password,switch
# but check, date and time are not set as subviews of cell.content_view
for tf in cell.content_view.subviews:
if 'SegmentedControl' in str(type(tf)):
item = cc.sections[s][1][i] # section s, 1=items, row i
if tf.selected_index >= 0:
cc.values[tf.name] = item['segments'][tf.selected_index]
return cc.values
def base_button_action(sender):
global cc
cc.base = True
cc.container_view.close()
def calc_button_action(sender):
global cc
cc.calc = True
cc.container_view.close()
def scan_button_action(sender):
global cc
cc.scan = True
cc.container_view.close()
def graphique_button_action(sender):
global cc
w = cc.container_view['graphic_area'].width/(326/2)
h = cc.container_view['graphic_area'].height/(326/2)
h = 6
w = h * (cc.container_view['graphic_area'].width/cc.container_view['graphic_area'].height)
# figsize is in inch
# ipad and ipad mini:
# pixels: 2048 x 1536
# dpi: 264 or 326 (mini)
# fonts for texts
font = {'family' : 'serif','color' : 'darkred','weight' : 'normal','size' : 16,}
fig,ax = plt.subplots(figsize=(w,h),dpi=326) # return figure and axes
#plt.title('test')#, fontdict=fonttitle)
plt.subplots_adjust(left=0.1,right=0.9)
plt.xlabel('', fontdict=font)
plt.ylabel(cc.ylabel, fontdict=font)
for curve in range(len(cc.graphic_data)):
x = []
y = []
for ymd,wgt in cc.graphic_data[curve]:
x.append(mdates.date2num(datetime.strptime(ymd,'%Y%m%d')))
y.append(wgt)
ax.plot_date(x,y,'-', label=cc.legends[curve],linewidth=2, color=['blue','red'][curve])
if len(x) == 1:
plt.scatter(x[0],y[0], s=40, c=['blue','red'][curve], marker=['o','s','+'][curve])
plt.legend(loc=6,fontsize=14)
# format the ticks
if len(x) == 1:
dt = datetime.strptime(ymd,'%Y%m%d')
dmy = dt.strftime('%d/%m/%Y')
ax.set_xticks(x)
ax.set_xticklabels([dmy])
else:
formatter = mdates.DateFormatter("%d/%m/%Y")
ax.xaxis.set_major_formatter(formatter)
locator = mdates.WeekdayLocator(byweekday=mdates.MO)
ax.xaxis.set_major_locator(locator)
plt.gcf().autofmt_xdate()
ax.yaxis.tick_left()
ax2 = ax.twinx()
ax2.set_ylim(ax.get_ylim())
ax2.yaxis.tick_right()
ax2.set_ylabel(ax.get_ylabel(),fontdict=font)
ax.grid(True)
b = BytesIO()
plt.savefig(b)
plt.close(fig) # free memory of figure
cc.container_view['graphic_area'].image = ui.Image.from_data(b.getvalue())
def poids_button_action(sender=None):
global MainView,cc
if not sender:
# called by a row selection
title = "Modification ou suppression d'un poids"
section,row = cc.view.selected_row
item = cc.sections[section][1][row]
dmy = item['title'] #dd/mm/yyyy
dt = datetime.strptime(dmy, '%d/%m/%Y ')
key = item.get('key', item.get('title', None))
wgt = cc.values[key].replace('kg','').strip()
wg = wgt.replace(',','.')
wg = float(wg)
else:
# called by add button
title = 'Ajoute Poids'
dt = datetime.now()
wgt = ''
fields = []
fields.append({'title':'date', 'type':'date', 'format':'%d/%m/%Y', 'value':dt})
fields.append({'title':'poids', 'type':'number', 'value':wgt})
if not sender:
# called by a row selection
fields.append({'title':'suppression?', 'type':'switch'})
f = dialogs.form_dialog(title=title, fields=fields, done_button_title='ok')
#print(f)
if not f:
# cancel button
return
if not sender:
# called by a row selection
if f['suppression?']:
# delete asked
b = console.alert('suppression', 'confirmez-vous?', 'oui', 'non', hide_cancel_button=True)
if b == 2:
console.alert('suppression annulée', '', 'ok', hide_cancel_button=True)
return
dtnew = f['date'] # datetime
#........try.............
try:
wgnew = f['poids'].replace(',','.')
wgnew = float(wgnew)
except:
console.alert('poids incorrect', '', 'ok', hide_cancel_button=True)
return
if not sender:
# called by a row selection
if f['suppression?']:
# delete asked
cc.poids = ('del',(dt,wg))
else:
# update
if dtnew == dt and wgnew == wg:
console.alert('modification inutile', 'même date et même poids', 'ok', hide_cancel_button=True)
return
if dtnew != dt:
# date modifiêe
ymd = dtnew.strftime('%Y%m%d')
MainView.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Poids', ymd))
if MainView.sql_cursor.fetchone():
# record Poids of this date already exists
console.alert('modification impossible', 'cette nouvelle date existe déjà', 'ok', hide_cancel_button=True)
return
cc.poids = ('upd',(dt,wg,dtnew,wgnew))
else:
# add
ymd = dtnew.strftime('%Y%m%d')
MainView.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Poids', ymd))
if MainView.sql_cursor.fetchone():
# record Poids of this date already exists
console.alert('ajoute impossible', 'cette date existe déjà', 'ok', hide_cancel_button=True)
return
cc.poids = ('add',(dtnew,wgnew))
ui.delay(cc.container_view.close,0.1)
def web_button_action(sender):
global cc
wv = ui.WebView()
wv.frame = cc.container_view.frame
wv.load_url(cc.url)
wv.present('sheet')
wv.wait_modal()
class MyView(ui.View):
def __init__(self,w,h):
self.width = w
self.height = h
o = ObjCClass('UIImage').systemImageNamed_('barcode')
with ui.ImageContext(32,32) as ctx:
o.drawAtPoint_(CGPoint(0,0))
UIImagePNGRepresentation = c.UIImagePNGRepresentation
UIImagePNGRepresentation.restype = c_void_p
UIImagePNGRepresentation.argtypes = [c_void_p]
self.UIImage_data = nsdata_to_bytes(ObjCInstance(UIImagePNGRepresentation(o)))
b_db = ui.ButtonItem()
#b_param.image = ui.Image.named('iob:ios7_gear_outline_32')
b_db.title = '🛠'
b_db.tint_color = 'black'
b_db.action = self.db_action
b_param = ui.ButtonItem()
#b_param.image = ui.Image.named('iob:ios7_gear_outline_32')
b_param.title = '⚙️'
b_param.tint_color = 'black'
b_param.action = self.param_action
b_user = ui.ButtonItem()
#b_user.image = ui.Image.named('iob:ios7_person_outline_32')
b_user.title ='👤'
b_user.tint_color = 'black'
b_user.action = self.user_action
b_poids = ui.ButtonItem()
b_poids.title = '⚖️'
b_poids.action = self.poids_action
b_points = ui.ButtonItem()
b_points.title = '📅'
b_points.action = self.points_action
b_aliments = ui.ButtonItem()
b_aliments.title = '🍅'
b_aliments.action = self.aliments_action
b_zeropoints = ui.ButtonItem()
b_zeropoints.title = '🆓'
b_zeropoints.action = self.zeropoints_action
b_recettes = ui.ButtonItem()
b_recettes.title = '🍽'
b_recettes.action = self.recettes_action
self.left_button_items = (b_db, b_param, b_user, b_poids, b_points)
self.right_button_items = (b_aliments, b_zeropoints, b_recettes)
# day view: begin---------------------------------
self.jours = ['Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi','Dimanche']
day_view = ui.View(name='day_view')
day_view.frame = (0,0,w,h)
day_view.flex = 'WH'
day_view.border_width = 1
day_view.border_color = 'blue'
self.add_subview(day_view)
# | < | Lu 12 | ... | Di 18 | > |
d = 10
week_prev = ui.Button(name='week_prev')
week_prev.frame = (d,d,32,32)
week_prev.image = ui.Image.named('iob:ios7_arrow_back_32')
week_prev.action = self.week_prev_action
day_view.add_subview(week_prev)
week_next = ui.Button(name='week_next')
week_next.frame = (self.width-d-32,d,32,32)
week_next.image = ui.Image.named('iob:ios7_arrow_forward_32')
week_next.action = self.week_next_action
day_view.add_subview(week_next)
x_grid = week_prev.x + week_prev.width + d
w_grid = week_next.x - d - x_grid
days_grid = ui.View(name='days_grid')
days_grid.frame = (x_grid,d,w_grid,32)
days_grid.border_width = 1
days_grid.border_color = 'lightgray'
day_view.add_subview(days_grid)
w_day = int(w_grid/7)
x = 0
for iday in range(7):
if iday == 6:
w_day = days_grid.width - x
b = ui.Button(name='day'+str(iday))
b.font = ('Menlo',10)
if iday == 3:
b.tint_color = 'green'
else:
b.tint_color = 'lightgray'
b.frame = (x,0,w_day,32)
b.border_width = 1
b.border_color = 'lightgray'
b.action = self.day_button_action
days_grid.add_subview(b)
lp = ui.Label(name='label_pts')
lp.frame = (0,22,w_day,10)
lp.font = ('Menlo',8)
lp.alignment = ui.ALIGN_CENTER
b.add_subview(lp)
x = x + w_day - 1
y = days_grid.y+days_grid.height+2
dx = 2
nz = 6 # number columns
w = int((self.width-2*dx)/6) # width each column
d = 5
hc = 32
x = dx
for iz in range(0,nz):
if iz == (nz-1):
w = self.width - x - dx # align right last column
if iz == 0 or iz == 3:
v1 = ui.View()
if iz == 3:
ww = self.width - x - dx # align right last column
else:
ww = 3 * w - 2
v1.frame = (x,y,ww,hc)
v1.border_width = 1
v1.border_color = 'lightgray'
day_view.add_subview(v1)
l1 = ui.Label()
l1.frame = (d,d,ww-2*d,v1.height-2*d)
l1.text = ['jour','','','réserve hebdomadaire','',''][iz]
l1.alignment = ui.ALIGN_CENTER
v1.add_subview(l1)
v2 = ui.View()
v2.frame = (x,v1.y+v1.height-1,w,hc)
v2.border_width = 1
v2.border_color = 'lightgray'
day_view.add_subview(v2)
l2 = ui.Label()
l2.frame = (d,d,w-2*d,v1.height-2*d)
l2.text = ['capital','consommés','restant','matin','utilisés','pour demain'][iz]
l2.alignment = ui.ALIGN_CENTER
v2.add_subview(l2)
v3 = ui.View(name=l2.text)
v3.frame = (x,v2.y+v2.height-1,w,hc)
v3.border_width = 1
v3.border_color = 'lightgray'
day_view.add_subview(v3)
l3 = ui.Label(name='points')
l3.alignment= ui.ALIGN_RIGHT
l3.frame = (d,d,w-2*d,v1.height-2*d)
l3.text = ['1','2','3','4','5','6'][iz]
v3.add_subview(l3)
x = x + w - 1
pts_hebdo = ui.Label(name='pts_hebdo')
pts_hebdo.frame = (10,y,w,32)
day_view.add_subview(pts_hebdo)
pts_jour = ui.Label(name='pts_jour')
pts_jour.frame = (10+w+10,y,w,32)
day_view.add_subview(pts_jour)
pts_rest = ui.Label(name='pts_rest')
pts_rest.frame = (10+w+10+w+10,y,w,32)
day_view.add_subview(pts_rest)
y = v3.y + v3.height + 2
day_meals = ui.TableView(name='day_meals')
day_meals.row_height = 45
tblo = ObjCInstance(day_meals)
tblo.sectionHeaderHeight = 0
tblo.sectionFooterHeight = 0
day_meals.frame = (1,y,self.width-2,self.height-y-70)
day_meals.border_width = 4
day_meals.border_color = 'blue'
day_meals.data_source = self
day_meals.delegate = self
day_view.add_subview(day_meals)
# day view: end-----------------------------------
# db
x = sys.argv[0] # Script path and name
i = x.rfind('/') # index last /
db_path = x[:i+1] # expanded path of script
os.chdir(db_path)
self.full_path = db_path+'WeightWatchers.db'
# TABLE Calls fields (call,bands,date,origin,adr,gps)
self.sql_connect = sqlite3.connect(self.full_path, check_same_thread=False)
self.sql_cursor = self.sql_connect.cursor()
# Create db
self.CreateDB()
# add initial data
file = 'WeightWatchers.txt'
with open(file,mode='rt',encoding='utf-8') as fil:
records = fil.read().split('\n')[:-1]
recout = []
for record in records:
if record.startswith('#'):
continue
dbrec = record.split('|')
recout.append((dbrec[0],dbrec[1],dbrec[2],dbrec[3]))
#print(recout)
self.sql_cursor.executemany("INSERT INTO WeightWatchers VALUES (?,?,?,?)",recout)
self.sql_connect.commit()
#self.GetDB()
# Get user infos
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=?", ('User',))
record = self.sql_cursor.fetchone()
rec_type,rec_key,rec_subkey,rec_data = record
self.user = rec_key
self.user_infos = ast.literal_eval(rec_data)
self.meals = ['déjeuner','dîner','souper','collation']
#self.meals = ['matin','midi','soir','collation']
self.units = ['gr','cl','pièce']
self.current_meal = 0
self.current_day = datetime.now().date()
self.set_day()
def day_button_action(self,sender):
self.current_day = sender.date
self.set_day()
def week_prev_action(self,sender):
self.current_monday += timedelta(days=-7)
self.set_week()
def week_next_action(self,sender):
self.current_monday += timedelta(days=+7)
self.set_week()
def set_day(self,ymd=None):
if ymd:
self.current_day = datetime.strptime(ymd, '%Y%m%d')
week_day = self.current_day.weekday()
# check if Points record exists
ymd = self.current_day.strftime('%Y%m%d')
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Points',ymd))
record = self.sql_cursor.fetchone()
if not record:
# Points record does not exist, create it
'''
record creation
si lundi
reserve hebdo matin = user info
else
si jour precedent n'existe pas
reserve hebdo matin = user info
else
reserve hebdo matin = celle du jour precedent
si points consommes le jour precedent = user info
pass
si points consommes le jour precedent < user info
reserve hebdo matin += max(4,user info - points veille)
si points consommes le jour precedent > user info
reserve hebdo matin -= max(reserve hebdo,points veille-user)
'''
if week_day == 0:
# monday
res_hebdo_matin = self.user_infos['réserve hebdo']
else:
# not a monday
ymd_prev = (self.current_day+timedelta(days=-1)).strftime('%Y%m%d')
# try to read the record of previous day
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Points',ymd_prev))
record = self.sql_cursor.fetchone()
if not record:
# record of previous day does not exist
res_hebdo_matin = self.user_infos['réserve hebdo']
else:
# record of previous day exists
rec_type,rec_key,rec_subkey,rec_data = record
res_hebdo_matin = int(rec_subkey) # this one of previous day
# compute number of points of this previous day
rec_data = ast.literal_eval(rec_data)
np = 0
for aliment in rec_data:
np += aliment['points']
if np == self.user_infos['points par jour']:
pass
elif np < self.user_infos['points par jour']:
res_hebdo_matin += min(4,self.user_infos['points par jour']-np)
elif np > self.user_infos['points par jour']:
res_hebdo_matin -= max(res_hebdo_matin,np-self.user_infos['points par jour'])
# create new record
self.sql_cursor.execute("INSERT INTO WeightWatchers VALUES (?,?,?,?)",('Points', ymd, res_hebdo_matin, str([])))
self.sql_connect.commit()
self.current_monday = self.current_day + timedelta(days=-week_day)
self.set_week()
def set_week(self):
current_visible = False
for iday in range(7):
dt = self.current_monday + timedelta(days=iday)
ymd = dt.strftime('%Y%m%d')
dmy = dt.strftime('%d/%m/%Y')
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Points',ymd))
record = self.sql_cursor.fetchone()
bn = 'day' + str(iday)
self['day_view']['days_grid'][bn].date = dt
self['day_view']['days_grid'][bn].title = f"{self.jours[iday][:2]} {dmy}"
if dt == self.current_day:
# show current day in week
cbg = 'lightgray'
cti = 'blue'
self.current_record = record
current_visible = True
else:
# other days of week
cbg = 'white'
# check if db record exists
if record:
# db record exists
oth_type,oth_key,oth_subkey,oth_data = record
oth_data = ast.literal_eval(oth_data)
np = 0
for aliment in oth_data:
np += aliment['points']
if np <= self.user_infos['points par jour']:
cti = 'green'
else:
cti = 'red'
self['day_view']['days_grid'][bn]['label_pts'].text = f"{np:2d} points"
else:
# db record does not exist
cti = 'gray'
self['day_view']['days_grid'][bn]['label_pts'].text = ''
# show if day record exists and if points are <= or > maximum of day
self['day_view']['days_grid'][bn].background_color = cbg
self['day_view']['days_grid'][bn].tint_color = cti
for sv in self['day_view'].subviews:
if sv.name not in ['days_grid','week_prev','week_next']:
sv.hidden = not current_visible
if not current_visible:
return
self.set_rows()
def set_rows(self):
rec_type,rec_key,rec_subkey,rec_data = self.current_record
# ['Points','yyyymmdd',reserve hebdo,[aliment,...]]
# aliment = {'repas':1, aliment':nom, 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')}
res_hebdo_matin = int(rec_subkey)
rec_data = ast.literal_eval(rec_data)
#print(rec_data)
pts_jour = 0
self.rows = []
for repas in range(1,5): # loop on meals
self.rows.append({'header':self.meals[repas-1]})
idx = len(self.rows)-1
np = 0
for aliment in rec_data:
if aliment['repas'] == repas:
np += aliment['points']
self.rows.append(aliment)
pts_jour += np
self.rows[idx]['points']=np
pts_rest = max(0, self.user_infos['points par jour']-pts_jour)
# ['capital','consommés','restant','matin','utilisés','pour demain']
self['day_view']['capital']['points'].text = str(self.user_infos['points par jour'])
self['day_view']['consommés']['points'].text = str(pts_jour)
if pts_jour > self.user_infos['points par jour']:
self['day_view']['consommés']['points'].text_color = 'red'
else:
self['day_view']['consommés']['points'].text_color = 'green'
self['day_view']['restant']['points'].text = str(max(self.user_infos['points par jour']-pts_jour,0))
self['day_view']['matin']['points'].text = str(res_hebdo_matin)
self['day_view']['utilisés']['points'].text = str(min(res_hebdo_matin,max(0,pts_jour-self.user_infos['points par jour'])))
if self.current_day.weekday() == 6: # Sunday
self['day_view']['pour demain']['points'].text = '0'
else:
self['day_view']['pour demain']['points'].text = str(min(self.user_infos['jour vers réserve'],int(self['day_view']['restant']['points'].text)))
self['day_view']['day_meals'].reload_data()
def tableview_number_of_sections(self, tableview):
return 1
def tableview_title_for_header(self, tableview, section):
return None
def tableview_title_for_footer(self, tableview, section):
return None
def tableview_number_of_rows(self, tableview, section):
return len(self.rows)
def tableview_cell_for_row(self, tableview, section, row):
cell = ui.TableViewCell()
cell.content_view.border_width = 1
cell.content_view.border_color = 'lightgray'
h = tableview.row_height
aliment = self.rows[row]
if 'header' in aliment:
# row is name of meal
cell.selectable = False
repas,np = self.rows[row]
cell.bg_color = 'lightgray'
t1 = aliment['header']
t2 = f"{aliment['points']:2d} points"
h1 = h
else:
# row is an aliment
cell.bg_color = 'white'
# 'repas':1, 'aliment':'viande', 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')
# or if zeropoint food
# 'repas':3, 'aliment':'Haricots secs', 'points':0, 'zeropoint':True
t1 = aliment['aliment']
if 'zeropoint' in aliment:
t2 = f"{aliment['points']:2d} points pour zeropoint"
else:
t2 = f"{aliment['points']:2d} points pour {aliment['quantité']:3d} {aliment['base'][1]}"
if 'recette' not in aliment:
h1 = h
else:
h1 = h*2/3
# title
l1 = ui.Label()
l1.text_color = 'black'
l1.font = ('Menlo',16)
l1.text = t1
w = (tableview.width - 3*5)*3/4
l1.frame = (5,0,w,h1)
cell.content_view.add_subview(l1)
# right subtitle
l2 = ui.Label()
l2.text_color = 'blue'
l2.font = ('Menlo',12)
l2.text = t2
x = l1.x + l1.width + 5
w = tableview.width - x - 5
l2.frame = (x,0,w,h)
cell.content_view.add_subview(l2)
if 'recette' in aliment:
# under subtitle
l3 = ui.Label()
l3.text_color = 'red'
l3.font = ('Menlo',10)
l3.text = 'ingrédient de la recette "' + aliment['recette'] + '"'
x = l1.x
w = l1.width
l3.frame = (x,l1.y+l1.height,w,h-2-h1)
cell.content_view.add_subview(l3)
selected_cell = ui.View()
selected_cell.bg_color = cell.bg_color
cell.selected_background_view = selected_cell
return cell
def tableview_did_select(self, tableview, section, row):
# a meal row should not be selectable...but tests show it is
#print(row)
aliment = self.rows[row]
if 'header' in aliment:
# row is name of meal
pass # no action
else:
# row is an aliment
product = aliment['aliment']
points = aliment['points']
quantity = aliment.get('quantité', None)
points_base = aliment.get('points base', None)
base = aliment.get('base', None)
self.current_meal = aliment['repas']-1
title = 'aliment du ' + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'day'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title), 0.1)
def CreateDB(self):
self.sql_connect.execute("DROP TABLE IF EXISTS WeightWatchers")
self.sql_cursor.execute("CREATE TABLE WeightWatchers (rec_type,rec_key,rec_subkey,rec_data)")
# zero points
file = 'WeightWatchers zero points.txt'
with open(file,mode='rt',encoding='utf-8') as fil:
zeropoints = fil.read().split('\n')[:-1]
section = ''
for aliment in zeropoints:
if aliment.startswith('@'):
groupe = aliment[1:]
else:
product = aliment
barcode = 'zeropoint'
points_base = 0
self.add_food(product, barcode, points_base, groupe=groupe)
def GetDB(self):
self.sql_cursor.execute("SELECT * FROM WeightWatchers")
self.all_records = self.sql_cursor.fetchall()
for record in self.all_records:
print(record)
def db_action(self,sender):
rec_types = {}
sections = []
# get all db records
self.sql_cursor.execute("SELECT * FROM WeightWatchers")
records = self.sql_cursor.fetchall()
for record in records:
rec_type,rec_key,rec_subkey,rec_data = record
if rec_type not in rec_types:
rec_types[rec_type] = []
rec_types[rec_type].append({'title':rec_key})
for rec_type in rec_types:
fields = rec_types[rec_type]
fields = sorted(fields, key=lambda k:k['title'])
sections.append((rec_type,fields))
del rec_types
# sort on rec_type
sort_order = ['User','Poids','Points','Recette','Aliment']
sections = sorted(sections, key=lambda k: sort_order.index(k[0]))
f = my_form_dialog(title='Liste des records de la db', sections=sections, dims=(self.width-60,self.height-10), search=True)
#print(f)
if not f:
# cancel button
return
section,row = f
rec_type = sections[section][0]
rec_key = sections[section][1][row]['title']
# Get record
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", (rec_type,rec_key))
record = self.sql_cursor.fetchone()
rec_type,rec_key,rec_subkey,rec_data = record
#rec_data = ast.literal_eval(rec_data)
tv = ui.TextView()
tv.frame = (0,0,500,500)
tv.font =('Menlo',15)
tv.text = rec_data
tv.name = rec_type + ':' + rec_key
tv.present('sheet')
tv.wait_modal()
def param_action(self,sender):
pass
def user_action(self,sender):
fields = []
fields.append({'title':'nom', 'type':'text', 'value':self.user})
fields.append({'title':'sexe', 'type':'text','value':self.user_infos['sexe'], 'segments':['femme','homme']})
t =str(self.user_infos['âge'])
fields.append({'title':'âge', 'type':'number', 'value':t})
t =str(self.user_infos['poids']).replace('.',',')
fields.append({'title':'poids (kg)', 'type':'number', 'value':t})
t =str(self.user_infos['taille']).replace('.',',')
fields.append({'title':'taille (m)', 'type':'number', 'value':t})
fields.append({'title':'plan', 'type':'text', 'value':self.user_infos['plan'], 'segments':['vert','bleu','violet']})
t =str(self.user_infos['points par jour'])
fields.append({'title':'points par jour', 'type':'number', 'value':t})
t =str(self.user_infos['réserve hebdo'])
fields.append({'title':'réserve hebdo', 'type':'number', 'value':t})
t =str(self.user_infos['jour vers réserve'])
fields.append({'title':'transfert max sur réserve', 'type':'number', 'value':t})
updated_fields = my_form_dialog(title='Utilisateur', fields=fields, editable=False)
#print(updated_fields)
def poids_action(self,sender):
while True:
fields = []
# get poids db records
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=?", ('Poids',))
records = self.sql_cursor.fetchall()
for record in records:
# 'Poids','yyyymmdd',poids,[]
rec_type,rec_key,rec_subkey,rec_data = record
ymd = rec_key
try:
wgt = float(rec_subkey)
recok = True
except:
recok = False
if not recok or type(wgt) is str:
# remove bad record
print('remove bad record ',rec_type,rec_key,rec_subkey)
#self.sql_cursor.execute("DELETE FROM WeightWatchers WHERE rec_type=? and rec_key=?",('Poids',rec_key))
#self.sql_connect.commit()
continue
val = '{:.2f}'.format(wgt).replace('.',',')+' kg'
dt = datetime.strptime(ymd, '%Y%m%d')
tt = dt.strftime('%d/%m/%Y ')
fields.append({'ymd':ymd, 'wgt':wgt, 'title':tt, 'type':'number', 'value':val})
if fields == []:
console.alert('aucun poids introduit', '', 'ok', hide_cancel_button=True)
return
# sort dates
fields = sorted(fields, key=lambda k: k['ymd'])
# build graphic data
graphic_data = [[]]
for field in fields:
graphic_data[0].append((field['ymd'], field['wgt']))
f = my_form_dialog(title='évolution du poids', fields=fields, graphique_button=False, graphic_data=graphic_data, poids_buttons=True, ui_image=True, dims=(self.width-60,self.height-10), editable=False, legends=['poids'], ylabel='Kg')
#print(f)
if not f:
# cancel button
break # leave loop
# returned is ('add',(datetime,weight))
if f[0] == 'add':
dt,wg = f[1]
ymd = dt.strftime('%Y%m%d')
# add poids record in db
self.sql_cursor.execute("INSERT INTO WeightWatchers VALUES (?,?,?,?)",('Poids',ymd,str(wg),str([])))
self.sql_connect.commit()
elif f[0] == 'del':
dt,wg = f[1]
ymd = dt.strftime('%Y%m%d')
# del poids record in db
self.sql_cursor.execute("DELETE FROM WeightWatchers WHERE rec_type=? and rec_key=?",('Poids',ymd))
self.sql_connect.commit()
elif f[0] == 'upd':
dt,wg,dtnew,wgnew = f[1]
ymd = dt.strftime('%Y%m%d')
ymdnew = dtnew.strftime('%Y%m%d')
# upd poids record in db
self.sql_cursor.execute("UPDATE WeightWatchers SET rec_key=? , rec_subkey=? WHERE rec_type=? and rec_key=?",(ymdnew, str(wgnew), 'Poids', ymd))
self.sql_connect.commit()
def points_action(self,sender):
fields = []
# get points db records
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=?", ('Points',))
records = self.sql_cursor.fetchall()
for record in records:
# ['Points','yyyymmdd',reserve hebdo,[aliment,...]]
# aliment = {'repas':1, aliment':nom, 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')}
rec_type,rec_key,rec_subkey,rec_data = record
ymd = rec_key
rec_data = ast.literal_eval(rec_data)
pts = 0
for aliment in rec_data:
pts += aliment['points']
val = '{:2d}'.format(pts)
dt = datetime.strptime(ymd, '%Y%m%d')
tt = dt.strftime('%d/%m/%Y ')
fields.append({'ymd':ymd, 'pts':pts, 'title':tt, 'type':'text', 'value':val})
if fields == []:
console.alert('aucun jour déjà introduit', '', 'ok', hide_cancel_button=True)
return
# sort dates
fields = sorted(fields, key=lambda k: k['ymd'])
# build graphic data
graphic_data = [[],[]]
for field in fields:
graphic_data[0].append((field['ymd'], field['pts']))
graphic_data[1].append((field['ymd'], int(self.user_infos['points par jour'])))
f = my_form_dialog(title='Calendrier des jours WeightWatchers', fields=fields, graphique_button=False, graphic_data=graphic_data, ui_image=True, dims=(self.width-60,self.height-10), editable=False, legends=['points du jour', 'points maximum'], ylabel='points')
#print(f)
if not f:
# cancel button
return
section,row = f
self.set_day(ymd=fields[row]['ymd'])
def aliments_action(self,sender):
sections = []
fields = []
# get aliments db records
cursors = self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_subkey!=?", ('Aliment','zeropoint'))
wmax = 0
for record in cursors:
# 'Aliment', nom, barcode, {'points':12, 'base':(100,'gr'), kcal':359,'saturated_fat':0.5,'sugars':1.5,'proteins':12.5, 'thumb':None/data, 'portion':('pièce',7,'gr'), 'groupe':'féculents'}
rec_type,rec_key,rec_subkey,rec_data = record
#print(rec_data)
rec_data = ast.literal_eval(rec_data)
subtitle = ''
if 'points base' in rec_data:
subtitle += f"{rec_data['points base']:2d} points"
if 'base' in rec_data:
subtitle += f" / {rec_data['base'][0]:3d} {rec_data['base'][1]}"
thumb_data = rec_data.get('thumb',None)
if thumb_data:
icon = ui.Image.from_data(thumb_data)
wmax = max(wmax,icon.size[0]) # maximum thumbs width
else:
icon = None
fields.append({'title':rec_key, 'subtitle':subtitle, 'icon':icon})
for field in fields:
if field['icon']:
with ui.ImageContext(wmax,128) as ctx:
img = field['icon']
w = img.size[0]
img.draw((wmax-w)/2,0,w,128)
field['icon'] = ctx.get_image()
if fields == []:
console.alert('aucun aliment', '', 'ok', hide_cancel_button=True)
return
# sort aliments
fields = sorted(fields, key=lambda k: k['title'].lower())
sections.append(('Liste',fields))
f = my_form_dialog(title='Liste des aliments', sections=sections, base_button=True, calc_button=True, scan_button=True, dims=(self.width-60,self.height-10), search=True, row_height=32)
print('return from liste des aliments',f)
if not f:
# cancel button
return
if f == 'base':
# base button
ui.delay(self.base_action, 0.1)
return
if f == 'calc':
# ingredients button
ui.delay(self.ingredients_action, 0.1)
return
if f == 'scan':
# ingredients button
ui.delay(self.scan_action, 0.1)
return
section,row = f
product = sections[section][1][row]['title']
points = rec_data['points base']
quantity = rec_data['base'][0]
points_base = rec_data['points base']
base = rec_data['base']
title = "ajoute aliment au " + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'aliment'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title), 0.1)
def recettes_action(self,sender):
sections = []
fields = []
# get recettes db records
cursors = self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=?", ('Recette',))
for record in cursors:
# record: ('Recette', nom, '', data)
rec_type,rec_key,rec_subkey,rec_data = record
#rec_data = ast.literal_eval(rec_data)
fields.append({'title':rec_key})
if fields == []:
console.alert('aucune recette', '', 'ok', hide_cancel_button=True)
return
# sort aliments
fields = sorted(fields, key=lambda k: k['title'].lower())
sections.append(('Liste',fields))
f = my_form_dialog(title='Liste des recettes', sections=sections, dims=(self.width-60,self.height-10), search=True)
#print(f)
if not f:
# cancel button
return
section,row = f
recette = sections[section][1][row]['title']
# Get recette infos
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Recette',recette))
record = self.sql_cursor.fetchone()
rec_type,rec_key,rec_subkey,rec_data = record
rec_data = ast.literal_eval(rec_data)
#Recette|nom||[{'aliment':'emmenthal','quantité':20, 'points':5, 'points base':3, 'base':(100,'gr')}, {'aliment':'haché','quantité':100, 'points':8, 'points base':12, 'base':(100,'gr')}]
product = []
quantity = []
points = []
points_base = []
base = []
for aliment in rec_data:
print(aliment)
product.append(aliment['aliment'])
points.append(aliment['points'])
quantity.append(aliment['quantité'])
points_base.append(aliment['points base'])
base.append(aliment['base'])
title = "Ajoute recette au " + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'aliment'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title, recette=recette), 0.1)
def zeropoints_action(self,sender):
zeropoints = {}
# get aliments db records
cursors = self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_subkey=?", ('Aliment','zeropoint'))
for record in cursors:
# record: ('Aliment', product, 'zeropoint',[...,...,...,groupe]
rec_type,rec_key,rec_subkey,rec_data = record
product = rec_key
rec_data = ast.literal_eval(rec_data)
groupe = rec_data['groupe']
if groupe not in zeropoints:
zeropoints[groupe] = []
zeropoints[groupe].append(product)
sections = []
for groupe in zeropoints:
fields = []
for aliment in zeropoints[groupe]:
fields.append({'title':aliment})
sections.append((groupe,fields))
f = my_form_dialog(title='Liste des aliments à zero point', sections=sections, dims=(self.width-60,self.height-10),search=True)
if not f:
# cancel button
return
section,row = f
product = sections[section][1][row]['title']
points = 0
quantity = None
points_base = 0
base = None
title = "ajoute aliment 'zero point' au " + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'zeropoints'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title), 0.1)
def base_action(self):
# dialog with aliment base infos
sections = []
fields = []
fields.append({'title':'Nom', 'type':'text'})
sections.append(('Nom',fields))
fields = []
fields.append({'title':'Quantité', 'type':'number'})
fields.append({'title':'Unité', 'type':'text', 'value':'gr', 'segments':self.units})
sections.append(('Pour',fields))
fields = []
fields.append({'title':'Points', 'type':'number'})
sections.append(('Points',fields))
f = my_form_dialog(title='Introduction de nouvel aliment sans ses ingrédients', sections=sections, dims=(self.width-60,self.height-10), editable=True)
#print(f)
if not f:
# cancel button
return
product = f['Nom']
barcode = ''
base = (int(f['Quantité']), f['Unité'])
points_base = int(f['Points'])
# add aliment to db
self.add_food(product, barcode, points_base, base=base)
# display immediately usage screen
#product known
points = points_base
quantity = base[0]
#points_base known
#base known
title = "ajoute aliment au " + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'aliment'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title), 0.1)
def ingredients_action(self):
# dialog with nutriments/image/smartpoints
sections = []
fields = []
fields.append({'title':'Nom', 'type':'text'})
sections.append(('Nom',fields))
fields = []
fields.append({'title':'Kcal', 'type':'number'})
fields.append({'title':'Graisses saturées', 'type':'text'})
fields.append({'title':'Sucre', 'type':'number'})
fields.append({'title':'Protéines', 'type':'number'})
sections.append(('Ingrédients',fields))
fields = []
fields.append({'title':'Quantité', 'type':'number'})
fields.append({'title':'Unité', 'type':'text', 'value':'gr', 'segments':self.units})
sections.append(('Pour',fields))
fields = []
fields.append({'title':'Points', 'type':'number'})
sections.append(('Points',fields))
f = my_form_dialog(title='Introduction de nouvel aliment par ses ingrédients', sections=sections, dims=(self.width-60,self.height-10), editable=True)
print(f)
if not f:
# cancel button
return
product = f['Nom']
barcode = ''
kcal = int(f['Kcal'])
saturated_fat = int(f['Graisses saturées'])
sugars = int(f['Sucre'])
proteins = int(f['Protéines'])
base = (int(f['Quantité']), f['Unité'])
points_base = int(f['Points'])
# add aliment to db
self.add_food(product, barcode, points_base, base=base, kcal=kcal, saturated_fat=saturated_fat, sugars=sugars, proteins=proteins)
# display immediately usage screen
#product known
points = points_base
quantity = base[0]
#points_base known
#base known
title = "ajoute aliment au " + self.meals[self.current_meal] + ' du ' + self.current_day.strftime('%d/%m/%Y')
self.use_food_from = 'aliment'
ui.delay(partial(self.use_food,product,points,quantity,points_base, base, title=title), 0.1)
def scan_action(self):
#self.barcode = '8076809545846' # pâtes
#self.barcode = '5400141194678' # test
#self.barcode = '3255537505079' # rhum
#self.barcode = '5449000267412' # coca-cola
#self.barcode = '0695342612976' # speculoos
self.barcode = '3564700340837' # fromage fondu
#self.barcode = '5410908000128' # chimay dorée
#self.barcode = '5410228258698' # leffe blonde
# scan barcode
#self.scan_barcode()
#print(self.barcode)
# check if barcode not yet in db
self.exists = False
get_web = True
cursors = self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_subkey=?", ('Aliment',self.barcode))
for record in cursors:
# record exists
self.exists = True
b = console.alert('aliment déjà connu', "voulez-vous vérifier si des modifications ont eu lieu", 'oui', 'non', hide_cancel_button=True)
if b == 2:
get_web = False
# record: ('Aliment', product, data)
rec_type,rec_key,rec_subkey,rec_data = record
# barcode: rec_subkey
# data: str([kcal, saturated_fat, sugars, proteins, points, portion, sp_portion, thumb_data])
product = rec_key
kcal, saturated_fat, sugars, proteins, points, portion, sp_portion, thumb_data = ast.literal_eval(rec_data)
image = thumb_data != None
graphic_data = thumb_data
url = 'https://fr.openfoodfacts.org/produit/' + self.barcode
if get_web:
# get barcode product name
url = 'https://fr.openfoodfacts.org/produit/' + self.barcode
response = requests.get(url)
ct = str(response.content.decode('utf-8'))
if "Il n'y a pas de produit référencé pour le code barre" in ct:
# barcode not in fr.openfoodfacts.org
console.alert('barcode inconnu', '', 'ok', hide_cancel_button=True)
return
#print(c)
i = ct.find('<title>')
j = ct.find('</title>', i)
product = ct[i+7:j]
# Taille d'une portion
t = "Taille d'une portion :</span>"
i = ct.find(t)
if i >= 0:
j = ct.find('</p>',i)
portion = ct[i+len(t):j].strip() # 20 gr
else:
portion = None
# get product detail
url_det = 'https://world.openfoodfacts.org/api/v0/product/' + self.barcode + '.json'
response = requests.get(url_det)
ct = str(response.content.decode('utf-8'))
ct = json.loads(ct)
#print(c)
prod = ct['product']
ingr = {'proteins_100g':'?', 'saturated-fat_100g':'?', 'sugars_100g':'?', 'energy-kcal_100g':'?'}
if 'nutriments' not in prod:
pass
else:
nutr = prod['nutriments']
for k in nutr.keys():
#print(k,nutr[k])
used = False
for x in ingr:
if x in k and 'serving' not in k and '_value' not in k and '_unit' not in k:
used = True
break
if not used:
continue
#print(k,nutr[k])
ingr[k] = nutr[k]
if 'image_url' in prod:
url_img = prod['image_url']
image = True
response = requests.get(url_img)
graphic_data=response.content
# create a thumb
ui_image = ui.Image.from_data(graphic_data)
pil_image = ui2pil(ui_image)
wi,hi = pil_image.size
h = 128
w = int(h*wi/hi)
pil_image_icon = pil_image.resize((w,h))
with io.BytesIO() as bIO:
pil_image_icon.save(bIO, 'PNG')
thumb_data = bIO.getvalue()
else:
url_img = None
image = False
graphic_data = None
thumb_data = None
# energy may be defined in kcal or kj
if 'energy-kcal_100g' in nutr:
kcal = nutr['energy-kcal_100g']
elif 'energy-kj_100g' in nutr:
kcal = int(nutr['energy-kj_100g'] * 0.239)
elif 'alcohol_100g' in nutr:
kcal = int(nutr['alcohol_100g'] * 7) # %vol 1gr=7 kcal
else:
kcal = 0
saturated_fat = nutr.get('saturated-fat_100g',0)
sugars = nutr.get('sugars_100g',0)
proteins = nutr.get('proteins_100g',0)
# compute smartpoints
points = smartpoints(kcal, saturated_fat, sugars, proteins)
# dialog with nutriments/image/smartpoints
sections = []
fields = []
t = "{:10d} kcal".format(kcal)
fields.append({'title':'Kcal ', 'type':'text', 'value':t})
t = "{:10.2f} g".format(saturated_fat)
fields.append({'title':'Graisses saturées', 'type':'text', 'value':t})
t = "{:10.2f} g".format(sugars)
fields.append({'title':'Sucre ', 'type':'text', 'value':t})
t = "{:10.2f} g".format(proteins)
fields.append({'title':'Protéines ', 'type':'text', 'value':t})
sections.append(('par 100 gr',fields))
fields = []
t = "{:10d}".format(points)
fields.append({'title':'Points ', 'type':'text', 'value':t})
sections.append(('Smartpoints 100 gr',fields))
if portion:
fields = []
t = portion
fields.append({'title':'Portion ', 'type':'text', 'value':t})
sp_portion = int(points*(float(''.join(ch for ch in t if ch.isdigit()))/100))
t = "{:10d}".format(sp_portion)
fields.append({'title':'Points ', 'type':'text', 'value':t})
sections.append(('Portion',fields))
else:
sp_portion = 0
# save db record Aliment
self.add_food(product, self.barcode, kcal, saturated_fat, sugars, proteins, points, portion, sp_portion, thumb_data)
f = my_form_dialog(title=product, sections=sections, web_button=True, web_url=url, ui_image=image, graphic_data=graphic_data, dims=(self.width-60,self.height-10), editable=False)
return
def add_food(self, product, barcode, points_base, base=None, kcal=None, saturated_fat=None, sugars=None, proteins=None, portion=None, thumb=None, groupe=None):
# db record
# 'Aliment', nom, barcode, {'points base':12, 'base':(100,'gr'), 'kcal':359,'saturated_fat':0.5,'sugars':1.5,'proteins':12.5, 'thumb':data, 'portion':('pièce',7,'gr'), 'groupe':'féculents'}
self.sql_cursor.execute("SELECT * FROM WeightWatchers WHERE rec_type=? and rec_key=?", ('Aliment', product))
if self.sql_cursor.fetchone():
# record Poids of this date already exists
console.alert('modification impossible', 'cet aliment existe déjà', 'ok', hide_cancel_button=True)
return
data_dict = {'points base':points_base}
if base:
data_dict['base'] = base
if kcal:
data_dict['kcal'] = kcal
if saturated_fat:
data_dict['saturated_fat'] = saturated_fat
if sugars:
data_dict['sugars'] = sugars
if proteins:
data_dict['proteins'] = proteins
if thumb:
data_dict['thumb'] = thumb
if portion:
data_dict['portion'] = portion
if groupe:
data_dict['groupe'] = groupe
data_str = str(data_dict)
self.sql_cursor.execute("INSERT INTO WeightWatchers VALUES (?,?,?,?)", ('Aliment', product, barcode,data_str))
self.sql_connect.commit()
def use_food(self, product, points, quantity, points_base, base, title="Utilisation d'un aliment", recette=None):
sections = []
if recette:
# recette
row_height = 45
fields = []
fields.append({'title':recette})
sections.append(('Recette',fields))
fields = []
for i in range(len(product)):
subtitle = f"{points_base[i]:2d} points / {base[i][0]:3d} {base[i][1]}"
points_per_unit = points_base[i] / base[i][0]
fields.append({'title':product[i], 'key':str(i), 'points':points[i], 'quantity':quantity[i], 'subtitle':subtitle, 'units':base[i][1], 'points_per_unit':points_per_unit, 'type':'number', 'value':str(quantity[i])})
sections.append(('Ingrédients de la recette',fields))
else:
# aliment
row_height = None
fields = []
fields.append({'title':product})
if quantity:
title_quantity = 'quantité (en '+base[1]+')'
fields.append({'title':title_quantity, 'type':'number', 'value':str(quantity)})
sections.append(('Aliment',fields))
fields = []
if base:
title_points = f"{points_base:2d} points par {base[0]:3d} {base[1]}"
else:
title_points = 'aliment zeropoint'
fields.append({'title':title_points, 'type':'text', 'value':str(points), 'readonly':True})
sections.append(('Points',fields))
fields = []
fields.append({'title':'repas', 'type':'text', 'value':self.meals[self.current_meal], 'segments':self.meals})
sections.append(('Repas',fields))
if self.use_food_from == 'day':
fields = []
fields.append({'title':"suppression de l'aliment?", 'type':'switch', 'value':False})
sections.append(('Suppression',fields))
prev_current_meal = self.current_meal
f = my_form_dialog(title=title, sections=sections, dims=(self.width-60,self.height-10), row_height=row_height)
print('return from dialog in use_food',f)
if not f:
# cancel button
return
self.current_meal = self.meals.index(f['repas'])
if self.use_food_from == 'day':
row = self['day_view']['day_meals'].selected_row[1]
aliment = self.rows[row]
rec_type,rec_key,rec_subkey,rec_data = self.current_record
rec_data = ast.literal_eval(rec_data)
# ['Points','yyyymmdd',reserve hebdo,[aliment,...]]
# aliment = {'repas':1, aliment':nom, 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')}
idx = rec_data.index(aliment)
if f.get("suppression de l'aliment?",False):
# delete aliment possible and asked
b = console.alert("suppression de\n"+product, 'confirmez-vous?', 'oui', 'non', hide_cancel_button=True)
if b == 2:
console.alert('suppression annulée', '', 'ok', hide_cancel_button=True)
return
# delete confirmed
del rec_data[idx]
else:
# update aliment
aliment['repas'] = self.current_meal + 1
if quantity:
aliment['quantité'] = int(f[title_quantity])
aliment['points'] = int(f[title_points])
rec_data[idx] = aliment
# upd rec_data record in db
rec_data = str(rec_data)
self.current_record = (rec_type,rec_key,rec_subkey,rec_data)
self.sql_cursor.execute("UPDATE WeightWatchers SET rec_data=? WHERE rec_type=? and rec_key=?",(rec_data, rec_type, rec_key))
self.sql_connect.commit()
# re-display current day meals
self.set_rows()
elif self.use_food_from == 'zeropoints':
rec_type,rec_key,rec_subkey,rec_data = self.current_record
rec_data = ast.literal_eval(rec_data)
# {'repas':3, 'aliment':'Haricots secs', 'points':0, 'zeropoint':True}]
aliment = {}
aliment['aliment'] = product
aliment['repas'] = self.current_meal + 1
aliment['points'] = 0
aliment['zeropoint'] = True
rec_data.append(aliment)
rec_data = str(rec_data)
self.current_record = (rec_type,rec_key,rec_subkey,rec_data)
self.sql_cursor.execute("UPDATE WeightWatchers SET rec_data=? WHERE rec_type=? and rec_key=?",(rec_data, rec_type, rec_key))
self.sql_connect.commit()
# re-display current day meals
self.set_rows()
elif self.use_food_from == 'aliment':
rec_type,rec_key,rec_subkey,rec_data = self.current_record
rec_data = ast.literal_eval(rec_data)
# {'repas':1, aliment':nom, 'points':3, 'quantité':25,'points base':12, 'base':(100,'gr')}
aliment = {}
aliment['aliment'] = product
aliment['repas'] = self.current_meal + 1
aliment['quantité'] = int(f[title_quantity])
aliment['points'] = int(f[title_points])
aliment['points base'] = points_base
aliment['base'] = base
rec_data.append(aliment)
rec_data = str(rec_data)
self.current_record = (rec_type,rec_key,rec_subkey,rec_data)
self.sql_cursor.execute("UPDATE WeightWatchers SET rec_data=? WHERE rec_type=? and rec_key=?",(rec_data, rec_type, rec_key))
self.sql_connect.commit()
# re-display current day meals
self.set_rows()
def scan_barcode(self):
self.barcode = ''
delegate = MetadataDelegate.new()
scan_view = ui.View(frame=(0, 0, 400, 400))
self.scan_view = scan_view
scan_view.name = 'Scannez le Barcode'
session = AVCaptureSession.alloc().init()
device = AVCaptureDevice.defaultDeviceWithMediaType_('vide')
_input = AVCaptureDeviceInput.deviceInputWithDevice_error_(device, None)
if _input:
session.addInput_(_input)
else:
print('Failed to create input')
return
output = AVCaptureMetadataOutput.alloc().init()
queue = ObjCInstance(dispatch_get_current_queue())
output.setMetadataObjectsDelegate_queue_(delegate, queue)
session.addOutput_(output)
output.setMetadataObjectTypes_(output.availableMetadataObjectTypes())
prev_layer = AVCaptureVideoPreviewLayer.layerWithSession_(session)
prev_layer.frame = ObjCInstance(scan_view).bounds()
prev_layer.setVideoGravity_('AVLayerVideoGravityResizeAspectFill')
ObjCInstance(scan_view).layer().addSublayer_(prev_layer)
session.startRunning()
scan_view.present('sheet')
scan_view.wait_modal()
session.stopRunning()
delegate.release()
session.release()
output.release()
def will_close(self):
self.sql_connect.close()
self.copy_to_icloud()
@on_main_thread
def copy_to_icloud(self):
shutil.copy(self.full_path, '/private/var/mobile/Library/Mobile Documents/iCloud~com~omz-software~Pythonista3/Documents/WeightWatchers.db')
def main():
global MainView
#----- Main process -----
# Initializations
# Hide script
w, h = ui.get_screen_size()
MainView = MyView(w, h)
MainView.background_color='white'
MainView.name = 'Weight Watchers'
MainView.barcode = ''
# present main view
MainView.present('fullscreen', hide_title_bar=False) #, title_bar_color='yellow')
# Protect against import
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment