Kuidas säästa aega ja raha, luues automaatse söögikoha plaanija

Õige retsepti valimiseks õigel päeval kasutage Google'i kalendrit ja Google'i arvutustabelite API-sid.

Foto autor Rawpixel saidil Unsplash

Kas olete stressis ka siis, kui saate küsimuse “mis täna õhtusöögiks on?” Sa ei ole üksi. Ma arvan, et see on kõige rohkem küsitud küsimus, kuna kell lööb kell 16.00. Otsustada, mida süüa, võib olla tüütu katsumus. Eriti kui teil on väikeseid lapsi erinevate koolijärgsete tegevustega.

Iga päev supermarketisse mineku vältimiseks kirjutame tavaliselt menüü järgmise nädala retseptidega. Nii saame kõik oma toidupoed osta ühe supermarketi külastuse ajal. See säästab palju aega. Lisaks säästab see ka raha. Seda seetõttu, et puutume kokku kõigi supermarketite müüdavate trikkidega.

Terve nädala retseptide otsimine nõuab mõtlemist ja kavandamist. Peame arvestama kõigi pereliikmete söömiseelistustega. Lisaks on meil iga päev toiduvalmistamiseks piiratud aeg. Selle hõlbustamiseks ehitasin automaatse söögikoha planeerija, millel on järgmised funktsioonid:

  • eraldage minu ja mu naise tööplaan meie jagatud Google'i kalendritest
  • ekstraheerige Google'i arvutustabelist meie eelistatud retseptid,
  • korrake mõnda retsepti igal nädalal samal päeval
  • enne teiste retseptide kordamist jätke üks nädal vahele
  • Mulle meeldib toiduvalmistamine rohkem kui minu naisele. Nii et päevadel, kui ma ei oska süüa teha, peaksid retseptid olema lühikese ajaga
  • laadige nädala menüü üles Google'i kalendrisse

Hüppame kohe sisse.

Google'i kalendri API ja Google'i lehtede API kasutamine

Esiteks peame looma uue Google Cloud projekti. Enne kui saame selles projektis Google'i kalendrit ja lehti kasutada, peame lubama API-d. Seda saab järgmistel veebilehtedel väga hästi selgitada:

  • Google'i kalendri API lubamine
  • Google'i arvutustabelite API lubamine

Kui see on tehtud, jätkame vajalike Pythoni pakettide importimist.

impordi konfiguratsioon cfg-na
importida pandasid pd-na
impordi numpy kui np
saidilt pathlib import Path
alates datetime import datetime
alates datetime import timedelta
saidilt googleapiclient.discovery impordi ehitamine
saidilt google.oauth2 impordi teenus_konto

Seadistamine

Privaatsuse ja turvalisuse tagamiseks hoian mõned parameetrid eraldi config.py-failis. Impordime faili pseudonüümiga cfg. Neid parameetreid käsitlen allpool fiktiivsete väärtustega. Saate need oma rakenduse jaoks lisada koos teie jaoks oluliste väärtustega.

Reguleerimisala

Ulatustega määratleme Google'i kalendrite ja lehtede juurdepääsutasemed. Peame lugema ja kirjutama juurdepääsu nii kalendritele kui ka lehtedele. Seega kasutame allpool olevaid URL-e.

SCOPES = ['https://www.googleapis.com/auth/calendar'
          , 'https://www.googleapis.com/auth/spreadsheets']

Google'i lehe ID ja ulatus

SPREADSHEET_ID = 
RANGE = 'vastuvõtke! A: G'

Peame täpsustama Google'i lehe ID koos retseptidega. Lisaks täpsustame retsepte sisaldavate lehtede vahemiku.

Google'i lehtede ID leiate, kui teete hiire parema nupuga hiireklõpsu Google Drive'i lehel. Seejärel valige „Hankige jagatav link”. ID leiate aadressilt „https://drive.google.com/open?id=”.

Minu Google'i lehel “receten” sisaldavad veerud A kuni G teavet iga retsepti kohta. Allolev ekraanipilt näitab mõnda näidissisu. Seega tuleb RANGE seadistada väärtusele “vastuvõetav! A: G”.

Google'i kalendri ID-d

CALENDARID_1 = 
CALENDARID_2 = 
CALENDARID_WEEKMENU = 

Peame täpsustama Google'i kalendri ID-d, kust sündmusi saada. Veenduge, et teil on juurdepääs kõigile kalendritele, mida soovite kaasata. ID leiate, käivitades selle skripti API-de Explorerist.

Selle projekti jaoks eraldame ainult kahe kalendri sündmused. Kuid võite koodi kohandada nii, et see hõlmaks rohkem kalendreid. Olen retseptide üleslaadimiseks loonud ka eraldi kalendri.

Ürituste sildid

BUSY_EVENTS = []
FREE_EVENTS = []
ALL_EVENTS = BUSY_EVENTS + FREE_EVENTS

Mu naine töötab vahetustega ja lisab need tähekoodide abil oma Google'i kalendrisse. Näiteks: B tähistab pärastlõunast vahetust. See sündmus on üks BUSY_EVENTS.

Kui mul on puhkepäev, lisan kalendrisse puhkepäeva. See sündmus on üks FREE_EVENTS.

Kõik sündmused on Google'i kalendrites terve päeva sündmused. Võite kasutada oma sündmuste siltide skeemi.

Traditsioonid

TRADITSIOONID = {
   'Neljapäev': 'friikartulid'
}

TRADITSIOONIDEGA mõtlen, et meie perel on nädalas paar päeva, mille jooksul koostame kindla retsepti. Kuna oleme pärit Belgiast, tähendab see friikartulite söömist kord nädalas (meie jaoks neljapäeval). Ja jah, enne kui teilt küsiti, see on friikartulid majoneesiga.

Oma sõnastuse traditsioone saate määratleda sõnaraamatus, võtmeks on päeva nimi ja väärtuseks retsept.

Päevade arv ette planeerida

Mõnikord ei saa me uue nädala menüü loomise päeval supermarketisse minna. Edasiseks planeerimiseks võib vaja minna mõnda päeva. NB_DAYS_BEFORE-ga anname endale veidi lõtvust. See tähendab, et uus nädala menüü genereeritakse teatud arv päevi enne eelmise nädala menüü lõppu.

NB_DAYS_BEFORE = 3

Teenuskonto kasutamine

Projekti API-de kasutamiseks kasutame teenuse kontot. Fail credentials.json on fail, mille saate API-de lubamisel alla laadida.

Loome mandaatide krediidi alloleva koodiga. Need mandaadid võimaldavad autentimist Google'i kalendrites ja Google'i lehel.

creds = service_account.Credentials.from_service_account_file ("credentials.json", ulatused = cfg.SCOPES)

Google'i kalendri sündmuste hankimine

Alustame teenuseobjekti loomisega ehitamismeetodil.

service_cal = build ('kalender', 'v3', mandaadid = kreedid)

Meid huvitavad ainult tuleva nädala sündmused. Nende sündmuste filtreerimiseks täpsustame kuupäevad ja vormindame need isoformat () abil. Parameetrid timeMin ja timeMax vajavad seda vormingut.

def format_date (kuupäev):
    date_time = datetime.combine (kuupäev, datetime.min.time ())
    date_time_utc = date_time.isoformat () + 'Z'
    tagastamise kuupäev_aja_aeg

Teenuseobjekti meetodi sündmuste (). Loendi abil eraldame sündmused. Seejärel filtreeritakse eraldatud sündmused HÕIVATUD ja TASUTA sündmuste jaoks. Kõik muud Google'i kalendrite sündmused pole selle projekti jaoks asjakohased. Hoiame algus- ja lõppkuupäeva ning sündmuste kokkuvõtet.

def get_event_date (sündmus, ajahetk):
    tagasi sündmus [ajahetk] .get ('dateTime', sündmus [ajapunkt] .get ('kuupäev'))

def get_events_by_calendarId (teenus, kalendri ID, timeMin, timeMax, allEvents):
    sündmuste_tulemus = teenus.events (). loend (kalenderId = kalenderId
                                        , timeMin = timeMin
                                        , timeMax = aegMax
                                        , singleEvents = tõsi
                                        , orderBy = 'startTime'). täitke ()
    events = events_result.get ('üksused', [])
    events_list = [(get_event_date (e, 'algus'), get_event_date (e, 'end'), e ['kokkuvõte']. ülemine ())
                   e üritustel
                   kui e ['kokkuvõte']. ülemine () kõigis sündmustes]
    tagastama lahtiürituste_loend (sündmuste_loend)

Mõned sündmused jagunesid rohkem kui üheks päevaks. Näiteks kui võtate puhkust rohkem kui üheks päevaks. Me tutvustame neid mitmepäevaseid sündmusi järgmise nädala jooksul igapäevasündmustena.

def unfold_events_list (sündmuste_loend):
    new_events_list = []
    e jaoks ürituste loendis:
        algus = datetime.strptime (e [0], '% Y-% m-% d'). kuupäev ()
        lõpp = datetime.strptime (e [1], '% Y-% m-% d'). kuupäev ()
        delta_päevad = (lõpp - algus) .päevad

        kui delta_päevad> 1:
            d vahemikus (deltapäevad):
                voltimata_päev = algus + ajakava (päevad = d)
                kui voltimata_päev> = kuupäevaaeg.nuu () .kuupäev () ja lahtirullitud_päev <= kuupäevaaeg.nuu () .kuupäev () + ajakava (päeva = 6):
                    new_events_list.append ((voltimata_päev, e [2]))
        muu:
            new_events_list.append ((algus, e [2]))
    tagasta new_events_list

Lõpuks soovime Panda DataFrame'i mõlema kalendri sündmustega eelolevaks nädalaks. Selle tulemuse saamiseks teisendame sündmuste loendid andmeraamideks ja liidame kuupäeval kokku. Ühendatud andmeraami lisame ka nädalapäeva.

def luua_tasemed_df (sündmused_loend_1, sündmused_loend_2):
    events_df_1 = pd.DataFrame.from_records (events_list_1, columns = ['date', 'events_cal_1'])
    events_df_2 = pd.DataFrame.from_records (events_list_2, columns = ['date', 'events_cal_2'])
    events_df = events_df_1.merge (events_df_2, on = 'kuupäev', kuidas = 'väline')
    events_df.date = pd.to_datetime (events_df.date)
    events_df.set_index ('kuupäev', inplace = tõene)
    events_df.sort_index (inplace = tõene)

    kuupäevad = loend (pd.perioodi_vahemik (START_DAY, NEXT_WEEK, sagedus = 'D'). väärtused)
    new_idx = []
    d kuupäevades:
        new_idx.append (np.datetime64 (d))

    events_df = üritused_df.reindex (new_idx)
    events_df.reset_index (inplace = tõene)
    events_df ['nädalapäev'] = events_df.date.apply (lambda x: x.strftime ('% A'))
    events_df.set_index ('kuupäev', inplace = tõene)
    tagasta sündmused_df

Veendumaks, et katame kõik tuleva nädala kõik kuupäevad, kasutame perioodivahemikku ja indekseerime ühendatud andmeraami uuesti.

Retseptide hankimine Google'i lehelt

Praegu on meil andmeraam tuleva nädala kõigi päevade ja kahes kalendris toimuvate sündmustega (kui neid on). Nüüd saame hakata retsepte Google'i lehelt ekstraheerima ja igale päevale retsepti määrama. Nagu Google'i kalendri API puhul, alustame teenuse Objekt API loomiseks teenuse Objekt loomist.

service_sheet = build ('lehed', 'v4', mandaadid = kreedid)

Meetodi tabeleid ().

def get_recipes (teenus, arvutustabelId, vahemik):
    recipes_result = service.spreadsheets () .väärtused (). get (spreadsheetId = spreadsheetId, range = range) .execute ()
    recipes = recipes_result.get ('väärtused', [])
    recipes_df = pd.DataFrame.from_records (retseptid [1:], veerud = retseptid [0])
    recipes_df.last_date_on_menu = pd.to_datetime (recipes_df.last_date_on_menu, dayfirst = True)
    recipes_df.set_index ('rea_number', inplace = tõene)
    itable_recipes = recipes_df [(recipes_df.last_date_on_menu 

Järgmisena loome retseptidega andmeraami. Mulle meeldib töötada Pandas DataFramesiga, kuid loomulikult võiksite kasutada ka muid andmestruktuure.

Rida_number on väli, mis arvutatakse Google Sheetis endas. Selle jaoks kasutame Google Sheet funktsiooni ROW (). See aitab värskendada välja last_date_on_menu õiges reas. Uuendame seda kuupäeva, kui järgmise nädala retsept on valitud.

Peame tagama, et retsepti korratakse alles ühe nädala pärast. Nii et me filtreerime retseptid_Filmi järgi viimase_date_on_menüü järgi. See kuupäev peab olema tühi või enne eelmist nädalat.

Nädala menüü koostamine

Selles etapis määrame järgmise nädala igaks päevaks sobiliku retsepti.

def generator_weekmenu (teenus, üritused_df, traditsioonid, tasuta_tapid):
    weekmenu_df = events_df.copy ()

    i, r jaoks sündmused_df.iterrows ():
        kui r.nädalapäev traditsioonides.võtmed ():
            weekmenu_df.loc [i, 'retsept'] = traditsioonid [r.nädalapäev]
            weekmenu_df.loc [i, 'description'] = ''
        muu:
            kui r.nädalapäev ['laupäeval', 'pühapäeval']:
                rida_number = vali_retsept ('keeruline', i, nädalamenüü_df, kõlblikud_retseptid)
                värskenduse leht (teenus, rea_number, i.strftime ('% d-% m-% Y'), vrd.SPREADSHEET_ID)
            elif r.events_cal_1 free_events või r.events_cal_2 free_events \
            või pd.isnull (r.events_cal_1) või pd.isnull (r.events_cal_2):
                rida_number = vali_retsept ('keskmine', i, nädalamenüü_df, kõlblikud_retseptid)
                värskenduse leht (teenus, rea_number, i.strftime ('% d-% m-% Y'), vrd.SPREADSHEET_ID)
            muu:
                rida_number = vali_retsept ('lihtne', i, nädalamenüü_df, kõlblikud_retseptid)
                värskenduse leht (teenus, rea_number, i.strftime ('% d-% m-% Y'), vrd.SPREADSHEET_ID)
    tagasi tagasi menüü_df

Töö planeerimise (HÕIVATUD ja TASUTA üritused) arvessevõtmiseks kasutame iga retsepti keerukust. Eelistatud raskuse juhuslik retsept lisatakse lehele weekmenu_df. Lõpuks jätame selle kõlblike retseptide hulgast välja, et samal nädalal dubleerivaid retsepte vältida.

def select_recipe (raskused, idx, nädalamenüü_df, kõlblikud_retseptid):
    choice_idx = np.random.choice (sobib_recipes.query ("raskused == '" + raskused + "'") .index.values)
    weekmenu_df.loc [idx, 'retsept'] = kõlblikud_retseptid.loc [valiku_idx, 'retsept']
    weekmenu_df.loc [idx, 'kirjeldus'] = kõlblikud_retseptid.loc [valiku_idx, 'kirjeldus']
    sobilikud_retseptid.drop (valiku_idx, kohapeal = tõene)
    tagasta valik_idx

Meetodi arvutustabelite () .väärtused (). Värskendus värskendab Google'i lehte.

def update_sheet (teenus, rea_number, kuupäev, spreadsheetId):
    range = "receten! F" + str (rea_number)
    väärtused = [[kuupäev]]
    keha = {'väärtused': väärtused}
    tulemus = teenus.spreadsheets () .väärtused (). värskendus (spreadsheetId = spreadsheetId
                                                    , vahemik = vahemik
                                                    , valueInputOption = 'USER_ENTERED'
                                                    , keha = keha) .execute ()

Me itreerime iga nädala menüü_df rea üle. Kui argipäev on üks TRADITIONS-nädalapäevadest, siis määrame vastava retsepti. Teistel tööpäevadel rakendame järgmist loogikat:

  • Nädalavahetusel vali keeruline retsept
  • Nädala jooksul, kui olen kodus või mu naisel on puhkepäev, valige keskmise raskusega retsept
  • Nädala jooksul, kui mina või mu naine tööl oleme, vali lihtne retsept

Nädalamenüü lisamine Google'i kalendrisse

Nüüd, kui meil on tuleva nädala menüü, saame selle sündmustena Google'i kalendrisse lisada. Olen selle jaoks eraldi kalendri loonud. Jagage seda kalendrit kliendi e-posti aadressiga saidil credentials.json. Samuti peate kalendri seadetes andma sellele loa sündmuste muutmiseks.

def add_weekmenu_to_calendar (teenus, weekmenu_df, calendarId):
    jaoks i, r nädalasmenu_df.iterrows ():
        sündmus = {
        'kokkuvõte': retsept,
        'kirjeldus': r.description,
        'Alusta': {
            'kuupäev': i.date (). isoformat (),
            'timeZone': 'Europe / Brüssel'
        },
        'lõpp': {
            'kuupäev': i.date (). isoformat (),
            'timeZone': 'Europe / Brüssel'
        }
        }
        sündmus = teenus.events (). sisesta (kalenderId = kalendri ID, keha = sündmus) .execute ()

Las automatiseeritakse

Siiani oleme võtnud arvesse kõiki rakenduse jaoks vajalikke funktsioone. Kuid nädala menüü koostamiseks peaksite ikkagi koodi käsitsi käitama.

Leidsin selle suurepärase veebisaidi PythonAnyWhere, kus saate Pythoni programme ajastada. Tasuta Algaja konto võimaldab iga päev planeerida ühte Pythoni programmi. See on täpselt see, mida me vajame.

Esiteks peame õmblema kõik funktsioonid kokku ja panema need Pythoni faili. Selles failis teen lisakontrolli, et näha, kus me praeguse nädala menüüs oleme. Ma teen seda, vaadates viimast kuupäeva retseptiga Google'i kalendris koos saidiga get_date_last_event.

def get_date_last_event (teenus, kalendri ID):
    sündmuste_tulemus = teenus.events (). loend (kalenderId = kalenderId
                                        , singleEvents = tõsi
                                        , orderBy = 'startTime'). täitke ()
    date_last_event = events_result.get ('üksused', []) [- 1] ['algus'] ['kuupäev'] [: 10]
    date_last_event = datetime.strptime (date_last_event, '% Y-% m-% d'). date ()
    tagastamise kuupäev_viimane sündmus

See kuupäev on salvestatud DATE_LAST_RECIPE. Kui praegune päev on pärast DATE_LAST_RECIPE miinus NB_DAYS_BEFORE, saame luua uue nädala menüü.

Kogu skripti leiate Githubist.

kui __name__ == '__main__':
    # Volituste saamine saidilt credentials.json
    CREDS_PATH = Path.cwd () / "weekmenu" / "credentials.json"
    creds = service_account.Credentials.from_service_account_file (CREDS_PATH, ulatused = cfg.SCOPES)

    # Teenindusobjektide loomine
    service_cal = build ('kalender', 'v3', mandaadid = kreedid)
    service_sheet = build ('lehed', 'v4', mandaadid = kreedid)

    # Kuupäevade määratlemine
    DATE_LAST_RECIPE = get_date_last_event (service_cal, cfg.CALENDARID_WEEKMENU)
    START_DAY = DATE_LAST_RECIPE + ajakava (päevades = 1)
    NEXT_WEEK = START_DAY + ajakava (päeva = 6)
    PREV_WEEK = START_DAY + ajakava (päevad = -7)
    START_DAY = format_date (START_DAY)
    NEXT_WEEK = vormingu kuupäev (NEXT_WEEK)
    PREV_WEEK = vormingu kuupäev (PREV_WEEK)

    # Retseptide hankimine Google'i lehelt
    retseptid_df, kõlblikud_retseptid = saada_retseptid (teenuse_leht, vrd.SPREADSHEET_ID, cfg.RANGE)

    # Kontrollige, kas eelmine nädala menüü on endiselt aktiivne
    kui DATE_LAST_RECIPE - kellaaeg (päevades = cfg.NB_DAYS_BEFORE) 

Rakenduses PythonAnyWhere olen loonud alamkaustade nädala menüü. Laadisin üles järgmised failid config.py, generator_weekmenu.py ja credentials.json.

Projektifailid saidil PythonAnyWhere.com

Seejärel plaanin igapäevast ülesannet, mille käigus käivitatakse jaotises Ülesanded skript genereerida_veekmenu.py. Ja voilà, me oleme kõik valmis.

Tulemus

Pärast skripti esimest käitamist on meie ühises Google'i kalendris tore menüü.

Automatiseeritud nädala menüü jagatud Google'i kalendris

Järeldus

See skript võtab arvesse teie ametialast ajakava Google'i kalendrites. See valib Google'i lehelt teie eelistatud retseptid. Skripti ajastades ilmuvad retseptid teie Google'i kalendrisse automatiseeritud viisil. See vabastab teid tüütust ettevõtmisest otsustada, mida süüa.

Kui soovite seda edasi viia, on siin mõned ideed skripti viimistlemiseks:

  • võtke arvesse retsepti küpsetusaega
  • lubada traditsiooni pidada vähemalt üks taimetoit nädalas
  • genereerige valitud retseptide jaoks toidupood

Loodan, et teile meeldis selle loo lugemine. Kui teil on skripti kohta küsimusi või ettepanekuid, võite kirjutada kommentaari allpool. Ja kui teile meeldis, siis plaksutage selle peale.