1363 lines
55 KiB
Plaintext
1363 lines
55 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"import re\r\n",
|
|
"import math\r\n",
|
|
"\r\n",
|
|
"from dataclasses import dataclass\r\n",
|
|
"\r\n",
|
|
"from copy import copy\r\n",
|
|
"\r\n",
|
|
"from openpyxl import load_workbook, Workbook\r\n",
|
|
"from openpyxl.utils import datetime\r\n",
|
|
"from openpyxl.styles import Color, PatternFill\r\n",
|
|
"from openpyxl.worksheet.table import Table\r\n",
|
|
"\r\n",
|
|
"from spielbergerscripts.spielbergerscripts.storageunit import Product, Bundle\r\n",
|
|
"from spielbergerscripts.spielbergerscripts.connector import IBMConnector"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# define some colors\r\n",
|
|
"\r\n",
|
|
"yellow = Color(rgb='FFFF00')\r\n",
|
|
"red = Color(rgb='E6B8B7')\r\n",
|
|
"orange = Color(rgb='FCD5B4')\r\n",
|
|
"purple = Color(rgb='CCC0DA')\r\n",
|
|
"blue = Color(rgb='B8CCE4')"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# classes Abpackung, DispoBedarf, Leistungsdaten, Umbau, Vorlage\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class Abpackung:\r\n",
|
|
" artikelNummer: int # ArtNr\r\n",
|
|
" abpackMenge: int # in KG\r\n",
|
|
" bemerkung: str\r\n",
|
|
" planPos: int # Wochenplannummer\r\n",
|
|
" bem_ek: str # Bemerkung Einkauf\r\n",
|
|
" roh1: str # Rohstoff 1\r\n",
|
|
" roh1_bes: str # Rohstoff 1 Bestand\r\n",
|
|
" roh2: str # Rohstoff 2\r\n",
|
|
" roh2_bes: str # Rohstoff 2 Bestand\r\n",
|
|
"\r\n",
|
|
" def anzBeutel(self, bundles):\r\n",
|
|
" gg = bundles[self.artikelNummer]\r\n",
|
|
" return int((self.abpackMenge * 1000) / gg.bagWeight)\r\n",
|
|
"\r\n",
|
|
" def anzGebinde(self, bundles):\r\n",
|
|
" gg = bundles[self.artikelNummer]\r\n",
|
|
" return int(self.anzBeutel(bundles) / gg.nbrBags)\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class DispoBedarf:\r\n",
|
|
" artikelNummer: str # ArtNr\r\n",
|
|
" palettenziel: int # Menge / Soll Palette\r\n",
|
|
" bestandL00: int # Cha BES00\r\n",
|
|
" bestandL01: int # Cha BES00\r\n",
|
|
" bestandL05: int # Cha BES05\r\n",
|
|
" auftragsBestand:int # Auf Bes\r\n",
|
|
" bestellBestand: int # Best Bes\r\n",
|
|
" durchschnitt: int # Durchschnitt Bestellung pro Woche\r\n",
|
|
" tuetenAnz: int # Anzahl Tüten Lager + HLB\r\n",
|
|
" bem_ek: str # Bemerkung Einkauf\r\n",
|
|
" roh1: str # Rohstoff 1\r\n",
|
|
" roh1_bes: str # Rohstoff 1 Bestand\r\n",
|
|
" roh2: str # Rohstoff 2\r\n",
|
|
" roh2_bes: str # Rohstoff 2 Bestand\r\n",
|
|
"\r\n",
|
|
" def bestandFrei(self):\r\n",
|
|
" return self.bestandL00 + self.bestandL05 + self.bestellBestand - self.auftragsBestand\r\n",
|
|
"\r\n",
|
|
" def reichweite(self, prodNeu = 0):\r\n",
|
|
" return (self.bestandFrei() + prodNeu) / self.durchschnitt\r\n",
|
|
"\r\n",
|
|
" def reichweiteNeu(self, abpackDict, prodDict):\r\n",
|
|
" abp = abpackDict.get(self.artikelNummer, None)\r\n",
|
|
" if abp is None:\r\n",
|
|
" return self.reichweite()\r\n",
|
|
"\r\n",
|
|
" return self.reichweite(abp.anzBeutel(prodDict))\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class Leistungsdaten:\r\n",
|
|
" artikelNummer: str\r\n",
|
|
" leistung: dict # Beutel / h pro Packerei (Pack1, Pack2, Pack3, Pack4)\r\n",
|
|
" modi: dict # Produktionsmodus pro Packerei\r\n",
|
|
" kurzBez: str\r\n",
|
|
" gebindeGr: Bundle\r\n",
|
|
" palettenMenge: int\r\n",
|
|
" muehle: bool\r\n",
|
|
" muehleMisch: bool\r\n",
|
|
" rezept: bool\r\n",
|
|
" gfProd: bool\r\n",
|
|
" duezi: bool\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class Umbauzeiten:\r\n",
|
|
" pack: str\r\n",
|
|
" modi: set\r\n",
|
|
" umbau: dict\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class PackereiVorlage:\r\n",
|
|
" packerei: str\r\n",
|
|
" beginn: datetime.datetime\r\n",
|
|
" vorlage: str"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# read Vorlage, Leistugnsdaten, Umbauzeiten, Stammdaten, Leistungsdaten\r\n",
|
|
"\r\n",
|
|
"def packVorlage(sheet):\r\n",
|
|
" vorlage = dict()\r\n",
|
|
" vorlage[\"Pack1\"] = PackereiVorlage(\r\n",
|
|
" packerei = sheet['A3'].value,\r\n",
|
|
" beginn = sheet['B3'].value,\r\n",
|
|
" vorlage = sheet['C3'].value,\r\n",
|
|
" )\r\n",
|
|
" vorlage[\"Pack2\"] = PackereiVorlage (\r\n",
|
|
" packerei = sheet['A4'].value,\r\n",
|
|
" beginn = sheet['B4'].value,\r\n",
|
|
" vorlage = sheet['C4'].value,\r\n",
|
|
" )\r\n",
|
|
" vorlage[\"Pack3\"] = PackereiVorlage (\r\n",
|
|
" packerei = sheet['A5'].value,\r\n",
|
|
" beginn = sheet['B5'].value,\r\n",
|
|
" vorlage = sheet['C5'].value,\r\n",
|
|
" )\r\n",
|
|
" vorlage[\"Pack4\"] = PackereiVorlage (\r\n",
|
|
" packerei = sheet['A6'].value,\r\n",
|
|
" beginn = sheet['B6'].value,\r\n",
|
|
" vorlage = sheet['C6'].value,\r\n",
|
|
" )\r\n",
|
|
"\r\n",
|
|
" return vorlage\r\n",
|
|
"\r\n",
|
|
"def getPackLeistung(artikelNummer):\r\n",
|
|
" return LEISTUNGS_DICT.get(artikelNummer, LEISTUNGS_DICT[\"000000\"]) \r\n",
|
|
"\r\n",
|
|
"def getWechsel(modus, new_modus, pack):\r\n",
|
|
" tWechsel = PROD_STAMM[pack].umbau.get((modus, new_modus), None)\r\n",
|
|
" if tWechsel is None:\r\n",
|
|
" return 20\r\n",
|
|
" else:\r\n",
|
|
" return tWechsel\r\n",
|
|
"\r\n",
|
|
"def shortName(artikelNummer):\r\n",
|
|
" return getPackLeistung(artikelNummer).kurzBez\r\n",
|
|
"\r\n",
|
|
"def beutelProH(artikelNummer, packerei):\r\n",
|
|
" return getPackLeistung(artikelNummer).leistung[packerei]\r\n",
|
|
"\r\n",
|
|
"def readProdStamm(sheet, pack, offset):\r\n",
|
|
" modi = []\r\n",
|
|
" count = 0\r\n",
|
|
" while True:\r\n",
|
|
" c = sheet.cell(row=offset + 2 + count, column = 2).value\r\n",
|
|
" if c is None:\r\n",
|
|
" break\r\n",
|
|
" modi.append(c)\r\n",
|
|
" count += 1\r\n",
|
|
" umbau = dict()\r\n",
|
|
" for idxI, modI in enumerate(modi):\r\n",
|
|
" for idxJ, modJ in enumerate(modi):\r\n",
|
|
" umbau[modI, modJ] = int(sheet.cell(row=offset + 2 + idxJ, column=6+idxI).value)\r\n",
|
|
" return Umbauzeiten(\r\n",
|
|
" pack,\r\n",
|
|
" modi,\r\n",
|
|
" umbau\r\n",
|
|
" )\r\n",
|
|
"\r\n",
|
|
"def produktionStammdaten(sheet):\r\n",
|
|
" return {\r\n",
|
|
" \"Pack1\": readProdStamm(sheet, \"Pack1\", 1),\r\n",
|
|
" \"Pack2\": readProdStamm(sheet, \"Pack2\", 11),\r\n",
|
|
" \"Pack3\": readProdStamm(sheet, \"Pack3\", 30),\r\n",
|
|
" \"Pack4\": readProdStamm(sheet, \"Pack4\", 36),\r\n",
|
|
" }\r\n",
|
|
"\r\n",
|
|
"def isSet(field):\r\n",
|
|
" if field is None:\r\n",
|
|
" return False\r\n",
|
|
" if field != 1:\r\n",
|
|
" return False\r\n",
|
|
" return True\r\n",
|
|
"\r\n",
|
|
"def packLeistung(sheet):\r\n",
|
|
" leistungs_dict = {}\r\n",
|
|
" for row in sheet.iter_rows(min_row=3, max_row=len(sheet['A']), max_col=18, values_only=True):\r\n",
|
|
" if row[0] is None:\r\n",
|
|
" continue\r\n",
|
|
" l = Leistungsdaten(\r\n",
|
|
" artikelNummer=row[0],\r\n",
|
|
" leistung={\r\n",
|
|
" \"Pack1\": int(row[1] if not (row[1] is None or row[1] == \"#N/A\") else 2000),\r\n",
|
|
" \"Pack2\": int(row[3] if not (row[3] is None or row[3] == \"#N/A\") else 1000),\r\n",
|
|
" \"Pack3\": int(row[5] if not (row[5] is None or row[5] == \"#N/A\") else 800),\r\n",
|
|
" \"Pack4\": int(row[7] if not (row[7] is None or row[7] == \"#N/A\") else 60)\r\n",
|
|
" },\r\n",
|
|
" modi = {\r\n",
|
|
" \"Pack1\": row[2],\r\n",
|
|
" \"Pack2\": row[4],\r\n",
|
|
" \"Pack3\": row[6],\r\n",
|
|
" \"Pack4\": row[8]\r\n",
|
|
" },\r\n",
|
|
" kurzBez=row[9],\r\n",
|
|
" gebindeGr=Bundle.fromString(row[10]),\r\n",
|
|
" palettenMenge=int(row[11]),\r\n",
|
|
" muehle = isSet(row[13]),\r\n",
|
|
" muehleMisch = isSet(row[14]),\r\n",
|
|
" rezept = isSet(row[15]),\r\n",
|
|
" gfProd = isSet(row[16]),\r\n",
|
|
" duezi = isSet(row[17])\r\n",
|
|
" )\r\n",
|
|
" leistungs_dict[l.artikelNummer] = l\r\n",
|
|
"\r\n",
|
|
" return leistungs_dict"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"tags": []
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# read Dispo\r\n",
|
|
"\r\n",
|
|
"def readDispo(sheet):\r\n",
|
|
" DISPO_DICT = {}\r\n",
|
|
" ABPACK_DICT = {}\r\n",
|
|
" GEB_GROESSE = {}\r\n",
|
|
" HEADER = True\r\n",
|
|
" for row in sheet.rows:\r\n",
|
|
" if HEADER: # skip the header lines\r\n",
|
|
" if row[0].value == \"ARTNR\":\r\n",
|
|
" HEADER = False\r\n",
|
|
" continue\r\n",
|
|
" if row[0].value is None:\r\n",
|
|
" break\r\n",
|
|
"\r\n",
|
|
" d = DispoBedarf(\r\n",
|
|
" artikelNummer=row[0].value,\r\n",
|
|
" palettenziel=row[4].value,\r\n",
|
|
" bestandL00=row[5].value,\r\n",
|
|
" bestandL01=row[6].value,\r\n",
|
|
" bestandL05=row[7].value,\r\n",
|
|
" auftragsBestand=row[8].value,\r\n",
|
|
" bestellBestand=row[15].value,\r\n",
|
|
" durchschnitt=row[17].value,\r\n",
|
|
" tuetenAnz= 0 if row[24].value is None else int(float(row[24].value)*1000),\r\n",
|
|
" roh1 = row[18].value,\r\n",
|
|
" roh1_bes = row[20].value,\r\n",
|
|
" roh2 = row[21].value,\r\n",
|
|
" roh2_bes = row[22].value,\r\n",
|
|
" bem_ek = row[23].value\r\n",
|
|
" )\r\n",
|
|
"\r\n",
|
|
" g = Bundle.fromString(row[2].value)\r\n",
|
|
"\r\n",
|
|
" DISPO_DICT[d.artikelNummer] = d\r\n",
|
|
" GEB_GROESSE[d.artikelNummer] = g\r\n",
|
|
"\r\n",
|
|
" if not (row[25].value is None):\r\n",
|
|
" a = Abpackung(\r\n",
|
|
" artikelNummer=d.artikelNummer,\r\n",
|
|
" abpackMenge=int(row[25].value),\r\n",
|
|
" bemerkung=\"\",\r\n",
|
|
" planPos= 999 if row[29].value is None else int(int(row[29].value)),\r\n",
|
|
" roh1 = row[18].value,\r\n",
|
|
" roh1_bes = row[20].value,\r\n",
|
|
" roh2 = row[21].value,\r\n",
|
|
" roh2_bes = row[22].value,\r\n",
|
|
" bem_ek = row[23].value\r\n",
|
|
" )\r\n",
|
|
"\r\n",
|
|
" ABPACK_DICT[d.artikelNummer]=a\r\n",
|
|
"\r\n",
|
|
" return DISPO_DICT, ABPACK_DICT, GEB_GROESSE"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# write Schedule\r\n",
|
|
"\r\n",
|
|
"def writeSchedule(vorlage, abpackDict, prodDict, gebGroesse, pack):\r\n",
|
|
" name = f\"Schedule {pack}\"\r\n",
|
|
"\r\n",
|
|
" sheet = vorlage.copy_worksheet(vorlage[\"Schedule\"])\r\n",
|
|
" if name in vorlage.sheetnames:\r\n",
|
|
" vorlage.remove(vorlage[name])\r\n",
|
|
" vorlage.move_sheet(sheet, 1 - vorlage.index(sheet))\r\n",
|
|
" sheet.title = name\r\n",
|
|
"\r\n",
|
|
" pas = list(abpackDict.values())\r\n",
|
|
" pas.sort(key=lambda pa: pa.planPos)\r\n",
|
|
" modus = None\r\n",
|
|
" time = 0\r\n",
|
|
" for pa in pas:\r\n",
|
|
" leistung = getPackLeistung(pa.artikelNummer)\r\n",
|
|
" if leistung.artikelNummer == \"000000\":\r\n",
|
|
" print(f\"Leistungsdaten für Produkt {pa.artikelNummer} nicht verfügbar!\")\r\n",
|
|
" \r\n",
|
|
" #print(f\"Trying to pack {pa.artikelNummer} with {beutelProH(pa.artikelNummer, pack)}\")\r\n",
|
|
" new_modus = leistung.modi[pack]\r\n",
|
|
" if time != 0:\r\n",
|
|
" if new_modus is None:\r\n",
|
|
" print(f\"Leistungsdaten für Produkt {pa.artikelNummer} auf Packerei {pack} nicht verfügbar!\")\r\n",
|
|
" time = writePAProdChange(sheet, time, 60, modus, f\"Unbekannt - Leistungsdaten für {pa.artikelNummer} auf {pack} setzen und Leistungsdaten aktualisieren!\")\r\n",
|
|
" else:\r\n",
|
|
" delta = getWechsel(modus, new_modus, pack)\r\n",
|
|
" time = writePAProdChange(sheet, time, delta, modus, new_modus)\r\n",
|
|
"\r\n",
|
|
" if pa.abpackMenge is None or pa.abpackMenge == 0:\r\n",
|
|
" print(f\"Für Produkt {pa.artikelNummer} in Pack {pack} ist keine gültige Abpackmenge eingetragen\")\r\n",
|
|
" continue\r\n",
|
|
"\r\n",
|
|
" time = writePASchedule(sheet, pa, leistung, prodDict, gebGroesse, pack, time)\r\n",
|
|
" modus = new_modus\r\n",
|
|
" writeEnd(sheet)\r\n",
|
|
"\r\n",
|
|
"def writeHeader(sheet):\r\n",
|
|
" sheet.cell(row=1, column = 1).value = \"ART.NR.\"\r\n",
|
|
" sheet.cell(row=1, column = 2).value = \"ART.BEZ.\"\r\n",
|
|
" sheet.cell(row=1, column = 3).value = \"GEBINDE\"\r\n",
|
|
" sheet.cell(row=1, column = 4).value = \"MENGE\"\r\n",
|
|
" sheet.cell(row=1, column = 5).value = \"GEBINDE\"\r\n",
|
|
" sheet.cell(row=1, column = 6).value = \"BEUTEL\"\r\n",
|
|
" sheet.cell(row=1, column = 7).value = \"PLANPOS\"\r\n",
|
|
" sheet.cell(row=1, column = 8).value = \"BEUTEL/H\"\r\n",
|
|
" sheet.cell(row=1, column = 9).value = \"DAUER\"\r\n",
|
|
" sheet.cell(row=1, column = 10).value = \"ANMERKUNG\"\r\n",
|
|
"\r\n",
|
|
"def writeEnd(sheet):\r\n",
|
|
" row = sheet.max_row + 1\r\n",
|
|
" sheet.cell(row=row, column = 9).value = \"30\"\r\n",
|
|
" sheet.cell(row=row, column = 10).value = \"Schichtende\"\r\n",
|
|
"\r\n",
|
|
"def prodTime(nbrBags, performance, min = 30):\r\n",
|
|
" return max(int(nbrBags * 60 / performance), 30)\r\n",
|
|
"\r\n",
|
|
"def writePASchedule(sheet, pa, l, prodDict, gebGroeDict, pack, start):\r\n",
|
|
" row = sheet.max_row + 1\r\n",
|
|
" geb = gebGroeDict[pa.artikelNummer]\r\n",
|
|
" prod = prodDict[pa.artikelNummer]\r\n",
|
|
" t = math.ceil(60 * pa.anzBeutel(gebGroeDict) / beutelProH(pa.artikelNummer, pack))\r\n",
|
|
" sheet.cell(row=row, column = 1).value = pa.artikelNummer\r\n",
|
|
" sheet.cell(row=row, column = 2).value = shortName(pa.artikelNummer)\r\n",
|
|
" sheet.cell(row=row, column = 3).value = geb.szBundle\r\n",
|
|
" sheet.cell(row=row, column = 4).value = pa.abpackMenge\r\n",
|
|
" sheet.cell(row=row, column = 5).value = pa.anzGebinde(gebGroeDict)\r\n",
|
|
" sheet.cell(row=row, column = 6).value = pa.anzBeutel(gebGroeDict)\r\n",
|
|
" sheet.cell(row=row, column = 7).value = pa.planPos\r\n",
|
|
" sheet.cell(row=row, column = 8).value = beutelProH(pa.artikelNummer, pack)\r\n",
|
|
" sheet.cell(row=row, column = 9).value = t\r\n",
|
|
" sheet.cell(row=row, column = 10).value = pa.bemerkung\r\n",
|
|
" sheet.cell(row=row, column = 11).value = pa.bem_ek\r\n",
|
|
" sheet.cell(row=row, column = 12).value = pa.roh1\r\n",
|
|
" sheet.cell(row=row, column = 13).value = pa.roh1_bes\r\n",
|
|
" sheet.cell(row=row, column = 14).value = pa.roh2\r\n",
|
|
" sheet.cell(row=row, column = 15).value = pa.roh2_bes\r\n",
|
|
"\r\n",
|
|
" if l.muehleMisch:\r\n",
|
|
" for i in range(1,10):\r\n",
|
|
" sheet.cell(row=row, column=i).fill = PatternFill(fgColor=purple, fill_type = 'solid')\r\n",
|
|
" if l.muehle:\r\n",
|
|
" for i in range(1,10):\r\n",
|
|
" sheet.cell(row=row, column=i).fill = PatternFill(fgColor=blue, fill_type = 'solid')\r\n",
|
|
"\r\n",
|
|
" return start + t\r\n",
|
|
"\r\n",
|
|
"def writePAProdChange(sheet, start, delta, modFrom, modTo):\r\n",
|
|
" row = sheet.max_row + 1\r\n",
|
|
" sheet.cell(row=row, column = 9).value = delta\r\n",
|
|
" if modFrom != modTo:\r\n",
|
|
" sheet.cell(row=row, column = 10).value = f\"Umbau auf {modTo}\"\r\n",
|
|
"\r\n",
|
|
" return start + delta"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Workday\r\n",
|
|
"@dataclass\r\n",
|
|
"class Workday:\r\n",
|
|
" begin: int\r\n",
|
|
" end: int\r\n",
|
|
" breaks: list\r\n",
|
|
"\r\n",
|
|
" def to_holliday():\r\n",
|
|
" return Workday(0, 0, [])\r\n",
|
|
"\r\n",
|
|
" def to_1_schicht_WU(begin, end=None):\r\n",
|
|
" return Workday(begin, begin + 9 if end is None else end, [3 * 60, 6 * 60])\r\n",
|
|
"\r\n",
|
|
" def to_1_schicht(begin, end=None):\r\n",
|
|
" return Workday(begin, begin + 9.5 if end is None else end, [2.5 * 60, 6 * 60])\r\n",
|
|
" \r\n",
|
|
" def to_2_schicht(begin, end=None):\r\n",
|
|
" return Workday(begin, begin + 18 if end is None else end, [3 * 60, 6.5 * 60, 12 * 60, 15.5 * 60])\r\n",
|
|
"\r\n",
|
|
" def to_2_schicht_one_only(begin, end=None):\r\n",
|
|
" return Workday(begin, begin + 9 if end is None else end, [3 * 60, 6.5 * 60])\r\n",
|
|
"\r\n",
|
|
" def to_3_schicht(begin, end=None):\r\n",
|
|
" return Workday(begin, begin + 24 if end is None else end, [3*60, 9 * 60, 12.5 * 60, 18 * 60, 21.5 * 60])\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class WTemplate:\r\n",
|
|
" template: str\r\n",
|
|
" packName: str\r\n",
|
|
" daySlots: int\r\n",
|
|
" dayStart: int\r\n",
|
|
" days: tuple\r\n",
|
|
"\r\n",
|
|
" def to_schicht_wu(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"1-Schicht-WU\",\r\n",
|
|
" packName,\r\n",
|
|
" 18,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_1_schicht_WU(begin)),\r\n",
|
|
" (\"DI\", Workday.to_1_schicht_WU(begin)),\r\n",
|
|
" (\"MI\", Workday.to_1_schicht_WU(begin)),\r\n",
|
|
" (\"DO\", Workday.to_1_schicht_WU(begin)),\r\n",
|
|
" (\"FR\", Workday.to_1_schicht_WU(begin)),\r\n",
|
|
" (\"SA\", Workday.to_1_schicht_WU(begin))\r\n",
|
|
" ),\r\n",
|
|
" )\r\n",
|
|
" \r\n",
|
|
" def to_schicht_long(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"1-Schicht\",\r\n",
|
|
" packName,\r\n",
|
|
" 19,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"DI\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"MI\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"DO\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"FR\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"SA\", Workday.to_1_schicht(begin))\r\n",
|
|
" ),\r\n",
|
|
" )\r\n",
|
|
" \r\n",
|
|
" def to_schicht_long_mischungen(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"1-Schicht+Mischungen\",\r\n",
|
|
" packName,\r\n",
|
|
" 19,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"DI\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"MI\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"DO\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"FR\", Workday.to_1_schicht(begin)),\r\n",
|
|
" (\"SA\", Workday.to_1_schicht(begin))\r\n",
|
|
" ),\r\n",
|
|
" )\r\n",
|
|
"\r\n",
|
|
" def to_2_schicht(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"2-Schicht\",\r\n",
|
|
" packName,\r\n",
|
|
" 36,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"DI\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"MI\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"DO\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"FR\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"SA\", Workday.to_2_schicht(begin)),\r\n",
|
|
" )\r\n",
|
|
" )\r\n",
|
|
" \r\n",
|
|
" def to_2_schicht_one_only(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"2-Schicht\",\r\n",
|
|
" packName,\r\n",
|
|
" 36,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"DI\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"MI\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"DO\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"FR\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"SA\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" )\r\n",
|
|
" )\r\n",
|
|
" \r\n",
|
|
" def to_2_schicht_special(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"2-Schicht\",\r\n",
|
|
" packName,\r\n",
|
|
" 36,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"DI\", Workday.to_2_schicht(begin)),\r\n",
|
|
" (\"MI\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"DO\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"FR\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" (\"SA\", Workday.to_2_schicht_one_only(begin)),\r\n",
|
|
" )\r\n",
|
|
" )\r\n",
|
|
" \r\n",
|
|
" def to_3_schicht(begin, packName):\r\n",
|
|
" return WTemplate(\r\n",
|
|
" \"3-Schicht\",\r\n",
|
|
" packName,\r\n",
|
|
" 36,\r\n",
|
|
" begin,\r\n",
|
|
" (\r\n",
|
|
" (\"MO\", Workday.to_3_schicht(begin)),\r\n",
|
|
" (\"DI\", Workday.to_3_schicht(begin)),\r\n",
|
|
" (\"MI\", Workday.to_3_schicht(begin)),\r\n",
|
|
" (\"DO\", Workday.to_3_schicht(begin)),\r\n",
|
|
" (\"FR\", Workday.to_3_schicht(begin)),\r\n",
|
|
" (\"SA\", Workday.to_3_schicht(begin)),\r\n",
|
|
" )\r\n",
|
|
" )\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Produktdatenbank aufbauen"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"ibmconnect = IBMConnector(True)\r\n",
|
|
"prodDict = ibmconnect.getProductsTable()\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Steuerungsdaten"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"KW = 39\r\n",
|
|
"\r\n",
|
|
"# ACHTUNG: Beim Lesen von der Dispo (FROM_DISPO = True) gehen alle bisherigen Planungen verloren!\r\n",
|
|
"FROM_DISPO = False # True oder False\r\n",
|
|
"\r\n",
|
|
"P1 = True\r\n",
|
|
"P2 = True\r\n",
|
|
"P3 = True\r\n",
|
|
"P4 = True\r\n",
|
|
"\r\n",
|
|
"#DISPO = f\"G:/Allgemein GmbH/Lager und Logistik/Dispo/Dispo Abpackung 2020/Dispo KW {KW-1}.xlsx\"\r\n",
|
|
"DISPO = f\"Dispo KW {KW-1}.xlsx\"\r\n",
|
|
"PLANUNG_PATH = f\"Wochenplanung KW {KW:02d}_temp.xlsx\"\r\n",
|
|
"FINAL_PATH = f\"Wochenplanung KW {KW:02d}.xlsx\"\r\n",
|
|
"VORLAGE = \"Abpackplanung Vorlage.xlsx\"\r\n",
|
|
"VORDEF = VORLAGE\r\n",
|
|
"\r\n",
|
|
"WEEK_P1 = WTemplate.to_2_schicht(5, \"Pack1\")\r\n",
|
|
"WEEK_P2 = WTemplate.to_2_schicht(5, \"Pack2\")\r\n",
|
|
"WEEK_P3 = WTemplate.to_schicht_wu(6, \"Pack3\")\r\n",
|
|
"WEEK_P4 = WTemplate.to_schicht_long(7, \"Pack4\")"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Leistungsdaten lesen"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"vorlage = load_workbook(filename = VORLAGE, data_only=True)\r\n",
|
|
"\r\n",
|
|
"print(\"Read Leistungsdaten\")\r\n",
|
|
"sheet = vorlage[\"Leistungsdaten\"]\r\n",
|
|
"PACK_VORLAGE = packVorlage(sheet)\r\n",
|
|
"LEISTUNGS_DICT = packLeistung(sheet)\r\n",
|
|
"\r\n",
|
|
"print(\"Read Produktionsstammdaten\")\r\n",
|
|
"sheet = vorlage[\"ProduktionStammdaten\"]\r\n",
|
|
"PROD_STAMM = produktionStammdaten(sheet)\r\n",
|
|
"\r\n",
|
|
"vorlage.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"tags": []
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Dispo lesen & Schedule erstellen"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# read Schedule from Dispo\r\n",
|
|
"if FROM_DISPO:\r\n",
|
|
" dispo = load_workbook(filename = DISPO, data_only = True, read_only = True)\r\n",
|
|
" if P1: dispoP1 = readDispo(dispo[\"Dispo Abpackplanung P1\"])\r\n",
|
|
" if P2: dispoP2 = readDispo(dispo[\"Dispo Abpackplanung P2\"])\r\n",
|
|
" if P3: dispoP3 = readDispo(dispo[\"Dispo Abpackplanung P3\"])\r\n",
|
|
" if P4: dispoP4 = readDispo(dispo[\"Dispo Abpackplanung P4\"])\r\n",
|
|
" dispo.close()\r\n",
|
|
"\r\n",
|
|
" vorlage = load_workbook(filename = VORDEF, data_only = False, read_only = False)\r\n",
|
|
" if P4: writeSchedule(vorlage, dispoP4[1], prodDict, dispoP4[2], \"Pack4\")\r\n",
|
|
" if P3: writeSchedule(vorlage, dispoP3[1], prodDict, dispoP3[2], \"Pack3\")\r\n",
|
|
" if P2: writeSchedule(vorlage, dispoP2[1], prodDict, dispoP2[2], \"Pack2\")\r\n",
|
|
" if P1: writeSchedule(vorlage, dispoP1[1], prodDict, dispoP1[2], \"Pack1\")\r\n",
|
|
" vorlage.save(PLANUNG_PATH)\r\n",
|
|
" vorlage.close() \r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"tags": []
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# Read update and write Schedule\r\n",
|
|
"\r\n",
|
|
"def readSchedule(sheet):\r\n",
|
|
" pas = []\r\n",
|
|
" for idx, row in enumerate(sheet.rows):\r\n",
|
|
" if idx == 0:\r\n",
|
|
" continue\r\n",
|
|
"\r\n",
|
|
" # switch between Produktionsauftrag und Umbau / Bemerkung\r\n",
|
|
" if row[0].value is None and row[1].value is None:\r\n",
|
|
" # Bemerkung / Umbau\r\n",
|
|
" pas.append(Bemerkung(\r\n",
|
|
" pDauer= int(row[8].value),\r\n",
|
|
" bemerkung= row[9].value,\r\n",
|
|
" marking = row[0].fill\r\n",
|
|
" ))\r\n",
|
|
" else: \r\n",
|
|
" l = LEISTUNGS_DICT[row[0].value]\r\n",
|
|
" pas.append(Produktionsauftrag(\r\n",
|
|
" artikelNummer= row[0].value,\r\n",
|
|
" artikelBezeichnung= row[1].value,\r\n",
|
|
" gebindeGroesse= row[2].value,\r\n",
|
|
" abpackMenge= int(row[3].value),\r\n",
|
|
" anzGebinde= int(row[4].value),\r\n",
|
|
" anzBeutel= int(row[5].value),\r\n",
|
|
" pNumber=int(row[6].value),\r\n",
|
|
" pLeistung= int(row[7].value),\r\n",
|
|
" pDauer= int(row[8].value),\r\n",
|
|
" bemerkung= row[9].value,\r\n",
|
|
" bem_ek=row[10].value,\r\n",
|
|
" roh1=row[11].value,\r\n",
|
|
" roh1_bes=row[12].value,\r\n",
|
|
" roh2=row[13].value,\r\n",
|
|
" roh2_bes=row[14].value,\r\n",
|
|
" marking = row[1].fill,\r\n",
|
|
" prodDZ = l.duezi,\r\n",
|
|
" ))\r\n",
|
|
" return pas\r\n",
|
|
"\r\n",
|
|
"def writePASchedule2(sheet, pa):\r\n",
|
|
" row = sheet.max_row + 1\r\n",
|
|
" \r\n",
|
|
" sheet.cell(row=row, column = 9).value = pa.pDauer\r\n",
|
|
" sheet.cell(row=row, column = 10).value = pa.bemerkung\r\n",
|
|
"\r\n",
|
|
" if isinstance(pa, Produktionsauftrag):\r\n",
|
|
" l = getPackLeistung(pa.artikelNummer)\r\n",
|
|
" if l.artikelNummer == \"000000\":\r\n",
|
|
" print(f\"Leistungsdaten für Produkt {pa.artikelNummer} nicht verfügbar!\")\r\n",
|
|
" \r\n",
|
|
" sheet.cell(row=row, column = 1).value = pa.artikelNummer\r\n",
|
|
" sheet.cell(row=row, column = 2).value = shortName(pa.artikelNummer)\r\n",
|
|
" sheet.cell(row=row, column = 3).value = pa.gebindeGroesse\r\n",
|
|
" sheet.cell(row=row, column = 4).value = pa.abpackMenge\r\n",
|
|
" sheet.cell(row=row, column = 5).value = pa.anzGebinde\r\n",
|
|
" sheet.cell(row=row, column = 6).value = pa.anzBeutel\r\n",
|
|
" sheet.cell(row=row, column = 7).value = pa.pNumber\r\n",
|
|
" sheet.cell(row=row, column = 8).value = pa.pLeistung\r\n",
|
|
" sheet.cell(row=row, column = 11).value = pa.bem_ek\r\n",
|
|
" sheet.cell(row=row, column = 12).value = pa.roh1\r\n",
|
|
" sheet.cell(row=row, column = 13).value = pa.roh1_bes\r\n",
|
|
" sheet.cell(row=row, column = 14).value = pa.roh2\r\n",
|
|
" sheet.cell(row=row, column = 15).value = pa.roh2_bes\r\n",
|
|
" if pa.marking.fgColor.rgb != \"00000000\":\r\n",
|
|
" for i in range(1,10):\r\n",
|
|
" sheet.cell(row=row, column=i).fill = pa.marking\r\n",
|
|
" else:\r\n",
|
|
" if l.muehleMisch:\r\n",
|
|
" for i in range(1,10):\r\n",
|
|
" sheet.cell(row=row, column=i).fill = PatternFill(fgColor=purple, fill_type = 'solid')\r\n",
|
|
" if l.muehle:\r\n",
|
|
" for i in range(1,10):\r\n",
|
|
" sheet.cell(row=row, column=i).fill = PatternFill(fgColor=blue, fill_type = 'solid')\r\n",
|
|
"# if l.duezi:\r\n",
|
|
"# for i in range(1,10):\r\n",
|
|
"# sheet.cell(row=row, column=i).fill = PatternFill(fgColor=orange, fill_type = 'solid')\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"def writeSchedule2(vorlage, pas , pack):\r\n",
|
|
" name = f\"Schedule {pack}\"\r\n",
|
|
"\r\n",
|
|
" sheet = vorlage.copy_worksheet(vorlage[\"Schedule\"])\r\n",
|
|
" if name in vorlage.sheetnames:\r\n",
|
|
" vorlage.remove(vorlage[name])\r\n",
|
|
" vorlage.move_sheet(sheet, -vorlage.index(sheet))\r\n",
|
|
" sheet.title = name\r\n",
|
|
"\r\n",
|
|
" for pa in pas:\r\n",
|
|
" #print(f\"Trying to pack {pa.artikelNummer} with {beutelProH(pa.artikelNummer, pack)}\")\r\n",
|
|
" if isinstance(pa, Produktionsauftrag):\r\n",
|
|
" if pa.abpackMenge is None or pa.abpackMenge == 0:\r\n",
|
|
" print(\"This should not happen ...\")\r\n",
|
|
" continue\r\n",
|
|
"\r\n",
|
|
" writePASchedule2(sheet, pa)\r\n",
|
|
"\r\n",
|
|
"def updateSchedule(sheet, pack):\r\n",
|
|
" pas = []\r\n",
|
|
" prodDictRev = {prod.name: prod for prod in prodDict.values()}\r\n",
|
|
" lastProd = 0\r\n",
|
|
"\r\n",
|
|
" for idx, row in enumerate(sheet.rows):\r\n",
|
|
" if idx == 0:\r\n",
|
|
" continue\r\n",
|
|
"\r\n",
|
|
" # switch between Produktionsauftrag und Umbau / Bemerkung\r\n",
|
|
" if row[0].value is None and row[1].value is None:\r\n",
|
|
" # Bemerkung / Umbau\r\n",
|
|
" pas.append(Bemerkung(\r\n",
|
|
" pDauer= int(row[8].value),\r\n",
|
|
" bemerkung= row[9].value,\r\n",
|
|
" marking = copy(row[0].fill)\r\n",
|
|
" ))\r\n",
|
|
" else:\r\n",
|
|
" assert row[0].value in prodDict or row[1].value in prodDictRev\r\n",
|
|
" assert row[3].value\r\n",
|
|
" prod = prodDictRev[row[1].value] if row[0].value is None else prodDict[row[0].value]\r\n",
|
|
" bundle = Bundle.fromString(prod.bundle)\r\n",
|
|
" amount = int(row[3].value)\r\n",
|
|
" nbrBags = int(amount * 1000 / bundle.bagWeight) if row[5].value is None else int(row[5].value)\r\n",
|
|
" nbrBundles = int(nbrBags / bundle.nbrBags) if row[4].value is None else int(row[4].value)\r\n",
|
|
" performance = int(row[7].value)\r\n",
|
|
" if performance is None:\r\n",
|
|
" l = getPackLeistung(prod.number)\r\n",
|
|
" if l.artikelNummer == \"000000\":\r\n",
|
|
" print(f\"Leistungsdaten für Produkt {prod.number} nicht verfügbar!\")\r\n",
|
|
" \r\n",
|
|
" performance = l.leistung.get(pack)\r\n",
|
|
" if performance is None:\r\n",
|
|
" print(f\"Leistungsdaten für Produkt {prod.number} auf Packerei {pack} nicht verfügbar!\")\r\n",
|
|
"\r\n",
|
|
" prodNr = lastProd + 1\r\n",
|
|
" if not row[6].value is None:\r\n",
|
|
" prodNr = int(row[6].value)\r\n",
|
|
" lastProdNr = prodNr\r\n",
|
|
"\r\n",
|
|
" p = prodDict[prod.number]\r\n",
|
|
" \r\n",
|
|
"\r\n",
|
|
" pas.append(Produktionsauftrag(\r\n",
|
|
" artikelNummer= prod.number,\r\n",
|
|
" artikelBezeichnung= prod.name,\r\n",
|
|
" gebindeGroesse= bundle.szBundle,\r\n",
|
|
" abpackMenge= amount,\r\n",
|
|
" anzGebinde= nbrBundles,\r\n",
|
|
" anzBeutel= nbrBags,\r\n",
|
|
" pNumber = prodNr,\r\n",
|
|
" pLeistung= performance,\r\n",
|
|
" pDauer= prodTime(nbrBags, performance) if row[8].value is None else row[8].value,\r\n",
|
|
" bemerkung= row[9].value,\r\n",
|
|
" bem_ek = row[10].value,\r\n",
|
|
" roh1=row[11].value,\r\n",
|
|
" roh1_bes=row[12].value,\r\n",
|
|
" roh2=row[13].value,\r\n",
|
|
" roh2_bes=row[14].value,\r\n",
|
|
" marking = copy(row[0].fill),\r\n",
|
|
" prodDZ = p.pickArea == \"33\"\r\n",
|
|
" ))\r\n",
|
|
" return pas"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Produktionsplanung schreiben\n",
|
|
"\n",
|
|
"nimmt die Schedule und schreibt sie in die Abpackplanungsdatei"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# classes Produktionsauftrag & Bemerkung\r\n",
|
|
"@dataclass\r\n",
|
|
"class Produktionsauftrag:\r\n",
|
|
" artikelNummer: str\r\n",
|
|
" artikelBezeichnung: str\r\n",
|
|
" gebindeGroesse: str\r\n",
|
|
" abpackMenge: int\r\n",
|
|
" anzGebinde: int\r\n",
|
|
" anzBeutel: int\r\n",
|
|
" pNumber: int\r\n",
|
|
" pLeistung: int\r\n",
|
|
" pDauer: int\r\n",
|
|
" bemerkung: str\r\n",
|
|
" bem_ek: str\r\n",
|
|
" roh1: str # Rohstoff 1\r\n",
|
|
" roh1_bes: str # Rohstoff 1 Bestand\r\n",
|
|
" roh2: str # Rohstoff 2\r\n",
|
|
" roh2_bes: str # Rohstoff 2 Bestand\r\n",
|
|
" marking: PatternFill\r\n",
|
|
" prodDZ: bool\r\n",
|
|
"\r\n",
|
|
"@dataclass\r\n",
|
|
"class Bemerkung:\r\n",
|
|
" pDauer: int\r\n",
|
|
" bemerkung: str\r\n",
|
|
" marking: PatternFill\r\n",
|
|
"\r\n",
|
|
" def isSimpleProdChange(self):\r\n",
|
|
" return self.bemerkung is None or self.bemerkung == \"\"\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# write Schedule to factory plan\r\n",
|
|
"\r\n",
|
|
"def toTimeString(minutes):\r\n",
|
|
" return f\"{int(minutes / 60):2}:{int(minutes % 60):02}\"\r\n",
|
|
"\r\n",
|
|
"def toTimeSlotString(minutes):\r\n",
|
|
" return f\"{toTimeString(minutes)} - {toTimeString(minutes + 30)}\"\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"def fillWorkday(sheet, kw, pack, workday):\r\n",
|
|
" # header\r\n",
|
|
" sheet.cell(row=2, column=2).value = kw\r\n",
|
|
" sheet.cell(row=3, column=1).value = pack\r\n",
|
|
"\r\n",
|
|
" current = workday.begin * 60\r\n",
|
|
" row = 4\r\n",
|
|
" while current < workday.end * 60:\r\n",
|
|
" sheet.cell(row=row, column=2).value = toTimeSlotString(current)\r\n",
|
|
" current += 30\r\n",
|
|
" if current >= 24*60:\r\n",
|
|
" current = 0\r\n",
|
|
" row += 1\r\n",
|
|
"\r\n",
|
|
"def writeToPlan(sheet, pa, day, startSlot, timeEnd, prodDict, workday):\r\n",
|
|
" if isinstance(pa, Produktionsauftrag):\r\n",
|
|
" writePA(sheet, pa, day, startSlot, prodDict, workday)\r\n",
|
|
" else:\r\n",
|
|
" writeComment(sheet, pa, day, startSlot, timeEnd, prodDict, workday)\r\n",
|
|
" \r\n",
|
|
"\r\n",
|
|
"def writeComment(sheet, pa, day, startSlot, timeEnd, prodDict, workday):\r\n",
|
|
" assert isinstance(pa, Bemerkung), \"Trying to write PA as Break\"\r\n",
|
|
"\r\n",
|
|
" if pa.pDauer < 30 or pa.bemerkung is None:\r\n",
|
|
" return\r\n",
|
|
"\r\n",
|
|
" columns = { \"MO\": 3, \"DI\": 13, \"MI\":23, \"DO\":33, \"FR\":43, \"SA\":53 }\r\n",
|
|
" columnOffset=columns[day]\r\n",
|
|
" rowOffset = 4\r\n",
|
|
"\r\n",
|
|
" lastSlot = slot(timeEnd)\r\n",
|
|
"\r\n",
|
|
" sheet.cell(row=rowOffset + startSlot, column=columnOffset+4).value=pa.bemerkung\r\n",
|
|
" current = startSlot\r\n",
|
|
" while True:\r\n",
|
|
" if not slotIsBreak(current, workday):\r\n",
|
|
" sheet.cell(row=rowOffset+current, column=columnOffset+4).fill = PatternFill(fgColor=red, fill_type = 'solid')\r\n",
|
|
" current += 1\r\n",
|
|
" if current >= lastSlot:\r\n",
|
|
" break\r\n",
|
|
"\r\n",
|
|
"def writePA(sheet, pa, day, startSlot, prodDict, workday):\r\n",
|
|
" assert isinstance(pa, Produktionsauftrag), \"Trying to write Break as PA\"\r\n",
|
|
" \r\n",
|
|
" columns = { \"MO\": 3, \"DI\": 13, \"MI\":23, \"DO\":33, \"FR\":43, \"SA\":53 }\r\n",
|
|
" columnOffset=columns[day]\r\n",
|
|
" rowOffset = 4\r\n",
|
|
" \r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+0).value = pa.anzBeutel\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+1).value = pa.anzGebinde\r\n",
|
|
" if pa.artikelNummer is None:\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+2).value = pa.gebindeGroesse\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+4).value = pa.artikelBezeichnung\r\n",
|
|
" else:\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+3).value = f\"p{pa.artikelNummer}\"\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+7).value = pa.abpackMenge\r\n",
|
|
" prod = prodDict.get(pa.artikelNummer, None)\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+6).value = math.ceil(pa.anzGebinde / prod.palSize) if not prod is None else None\r\n",
|
|
" if pa.bemerkung != \"\" and not pa.bemerkung is None and pa.pDauer >= 60:\r\n",
|
|
" sheet.cell(row=rowOffset + startSlot + 1, column=columnOffset+4).value=pa.bemerkung\r\n",
|
|
" sheet.cell(row=rowOffset + startSlot + 1, column=columnOffset+4).fill = PatternFill(fgColor=yellow, fill_type = 'solid')\r\n",
|
|
" if not pa.marking is None:\r\n",
|
|
" for i in range(8):\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+i).fill = pa.marking\r\n",
|
|
" if pa.prodDZ:\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+0).fill = PatternFill(fgColor=orange, fill_type = 'solid')\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+1).fill = PatternFill(fgColor=orange, fill_type = 'solid')\r\n",
|
|
" sheet.cell(row=rowOffset+startSlot, column=columnOffset+2).fill = PatternFill(fgColor=orange, fill_type = 'solid')\r\n",
|
|
"\r\n",
|
|
"def writeZeitVerschn(sheet, slotsPerDay, day, uebertragZ):\r\n",
|
|
" columns = { \"MO\": 3, \"DI\": 13, \"MI\":23, \"DO\":33, \"FR\":43, \"SA\":53 }\r\n",
|
|
" columnOffset=columns[day]\r\n",
|
|
" rowOffset = 4\r\n",
|
|
" sheet.cell(row=rowOffset+slotsPerDay, column=columnOffset+2).value = uebertragZ\r\n",
|
|
"\r\n",
|
|
" \r\n",
|
|
"def writeNegUebertrag(sheet, slotsPerDay, day, uebertrag):\r\n",
|
|
" columns = { \"MO\": 3, \"DI\": 13, \"MI\":23, \"DO\":33, \"FR\":43, \"SA\":53 }\r\n",
|
|
" columnOffset=columns[day]\r\n",
|
|
" rowOffset = 4\r\n",
|
|
" sheet.cell(row=rowOffset+slotsPerDay-1, column=columnOffset+0).value = -uebertrag\r\n",
|
|
" \r\n",
|
|
"def writePosUebertrag(sheet, slotsPerDay, day, uebertrag):\r\n",
|
|
" columns = { \"MO\": 3, \"DI\": 13, \"MI\":23, \"DO\":33, \"FR\":43, \"SA\":53 }\r\n",
|
|
" columnOffset=columns[day]\r\n",
|
|
" rowOffset = 4\r\n",
|
|
" sheet.cell(row=rowOffset, column=columnOffset+0).value = uebertrag\r\n",
|
|
" sheet.cell(row=rowOffset, column=columnOffset+1).value = \"Rest\""
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"# write Schedule to factory plan 2\r\n",
|
|
"def slot(time):\r\n",
|
|
" return max(0, math.floor(time / 30))\r\n",
|
|
"\r\n",
|
|
"def slotIsBreak(s, workday):\r\n",
|
|
" breakSlots = set(map(lambda b: slot(b), workday.breaks))\r\n",
|
|
"\r\n",
|
|
" return s in breakSlots\r\n",
|
|
"\r\n",
|
|
"def timeAdd(current, add, breaks):\r\n",
|
|
" newTime = current + add\r\n",
|
|
" # find the next break\r\n",
|
|
" nextBreak = 0\r\n",
|
|
" while nextBreak < len(breaks):\r\n",
|
|
" if breaks[nextBreak] > current:\r\n",
|
|
" break\r\n",
|
|
" nextBreak += 1\r\n",
|
|
"\r\n",
|
|
" # next break settled\r\n",
|
|
" for b in breaks[nextBreak:-1]:\r\n",
|
|
" if b <= newTime:\r\n",
|
|
" newTime += 30\r\n",
|
|
"\r\n",
|
|
" return (newTime, breaks[-1] - newTime)\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
"def writeAbpackplan(vorlage, wtemplate, schedule, prodDict):\r\n",
|
|
" days = wtemplate.days\r\n",
|
|
" sheet = vorlage.copy_worksheet(vorlage[wtemplate.template])\r\n",
|
|
" if wtemplate.packName in vorlage.sheetnames:\r\n",
|
|
" vorlage.remove(vorlage[wtemplate.packName])\r\n",
|
|
" vorlage.move_sheet(sheet, -vorlage.index(sheet))\r\n",
|
|
" sheet.title = wtemplate.packName\r\n",
|
|
"\r\n",
|
|
"\r\n",
|
|
" time = 0\r\n",
|
|
" paIdx = 0\r\n",
|
|
" remainingT = 0\r\n",
|
|
" uebertrag = None\r\n",
|
|
" fillWorkday(sheet, KW, wtemplate.packName, days[0][1])\r\n",
|
|
" \r\n",
|
|
" for day, schicht in wtemplate.days:\r\n",
|
|
" if schicht.begin == schicht.end:\r\n",
|
|
" # holliday\r\n",
|
|
" continue\r\n",
|
|
" \r\n",
|
|
" dayLength = (schicht.end - schicht.begin) * 60\r\n",
|
|
" breaks = schicht.breaks + [(schicht.end - schicht.begin) * 60,]\r\n",
|
|
" slotsPerDay = wtemplate.daySlots\r\n",
|
|
" time, remainingT = timeAdd(schicht.begin - wtemplate.dayStart, remainingT, breaks)\r\n",
|
|
" currentSlot = slot(time)\r\n",
|
|
" if not uebertrag is None:\r\n",
|
|
" writePosUebertrag(sheet, slotsPerDay, day, uebertrag)\r\n",
|
|
" uebertrag = None\r\n",
|
|
" \r\n",
|
|
" if remainingT < 0: # running into the next day\r\n",
|
|
" uebertragVerhältnis = -remainingT / currentPA.pDauer\r\n",
|
|
" uebertragMenge = int(uebertragVerhältnis * currentPA.anzBeutel)\r\n",
|
|
"\r\n",
|
|
" writeNegUebertrag(sheet, slotsPerDay + 1, day, uebertragMenge)\r\n",
|
|
" uebertrag = uebertragMenge\r\n",
|
|
"\r\n",
|
|
" remainingT = -remainingT\r\n",
|
|
" continue\r\n",
|
|
"\r\n",
|
|
" remainingT = max(30, remainingT)\r\n",
|
|
" \r\n",
|
|
"\r\n",
|
|
" while paIdx < len(schedule):\r\n",
|
|
" currentPA = schedule[paIdx] \r\n",
|
|
" if remainingT <= 15 and currentPA.pDauer > 30:\r\n",
|
|
" time = 0\r\n",
|
|
" break\r\n",
|
|
"\r\n",
|
|
" currentSlot = slot(time)\r\n",
|
|
" newtime, remainingT = timeAdd(time, currentPA.pDauer, breaks)\r\n",
|
|
"\r\n",
|
|
" writeToPlan(sheet, currentPA, day, currentSlot, newtime, prodDict, schicht)\r\n",
|
|
" lastSlot = currentSlot\r\n",
|
|
" \r\n",
|
|
" if remainingT < 0: # running into the next day\r\n",
|
|
" if remainingT < -15:\r\n",
|
|
" if type(currentPA) == Produktionsauftrag:\r\n",
|
|
" uebertragVerhältnis = -remainingT / currentPA.pDauer\r\n",
|
|
" uebertragMenge = int(uebertragVerhältnis * currentPA.anzBeutel)\r\n",
|
|
"\r\n",
|
|
" writeNegUebertrag(sheet, slotsPerDay + 1, day, uebertragMenge)\r\n",
|
|
" uebertrag = uebertragMenge\r\n",
|
|
" remainingT = -remainingT\r\n",
|
|
" else:\r\n",
|
|
" uebertrag = None\r\n",
|
|
" remainingT = 0\r\n",
|
|
"\r\n",
|
|
" paIdx += 1\r\n",
|
|
" break\r\n",
|
|
"\r\n",
|
|
" paIdx += 1\r\n",
|
|
" time = newtime\r\n",
|
|
"\r\n",
|
|
" if paIdx < len(schedule):\r\n",
|
|
" print(f\"Week is full!\")\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"tags": []
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Update Schedule"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"vorlage = load_workbook(filename = PLANUNG_PATH, data_only = False, read_only = False)\r\n",
|
|
"try:\r\n",
|
|
" if P4:\r\n",
|
|
" print(\"Updating P4 ...\")\r\n",
|
|
" scheduleP4 = updateSchedule(vorlage[\"Schedule Pack4\"], \"Pack4\")\r\n",
|
|
" writeSchedule2(vorlage, scheduleP4, \"Pack4\")\r\n",
|
|
" writeAbpackplan(vorlage, WEEK_P4, scheduleP4, prodDict)\r\n",
|
|
" if P3:\r\n",
|
|
" print(\"Updating P3 ...\")\r\n",
|
|
" scheduleP3 = updateSchedule(vorlage[\"Schedule Pack3\"], \"Pack3\")\r\n",
|
|
" writeSchedule2(vorlage, scheduleP3, \"Pack3\")\r\n",
|
|
" writeAbpackplan(vorlage, WEEK_P3, scheduleP3, prodDict)\r\n",
|
|
" if P2: \r\n",
|
|
" print(\"Updating P2 ...\")\r\n",
|
|
" scheduleP2 = updateSchedule(vorlage[\"Schedule Pack2\"], \"Pack1\")\r\n",
|
|
" writeSchedule2(vorlage, scheduleP2, \"Pack2\")\r\n",
|
|
" writeAbpackplan(vorlage, WEEK_P2, scheduleP2, prodDict)\r\n",
|
|
" if P1:\r\n",
|
|
" print(\"Updating P1 ...\")\r\n",
|
|
" scheduleP1 = updateSchedule(vorlage[\"Schedule Pack1\"], \"Pack1\")\r\n",
|
|
" writeSchedule2(vorlage, scheduleP1, \"Pack1\")\r\n",
|
|
" writeAbpackplan(vorlage, WEEK_P1, scheduleP1, prodDict)\r\n",
|
|
"\r\n",
|
|
" sheet = vorlage[\"Mischungen\"]\r\n",
|
|
" sheet[\"B2\"] = KW\r\n",
|
|
" \r\n",
|
|
" print(\"\\nWriting to file ...\")\r\n",
|
|
" vorlage.save(PLANUNG_PATH)\r\n",
|
|
" print(\"done!\")\r\n",
|
|
"except PermissionError:\r\n",
|
|
" print(f\"Saving the Excel file failed. Is it still open?\")\r\n",
|
|
"finally:\r\n",
|
|
" vorlage.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {
|
|
"tags": []
|
|
}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Produktionsplanung finalisieren"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"def finalizeSheet(sheet):\r\n",
|
|
" for row in sheet.iter_rows():\r\n",
|
|
" for cell in row:\r\n",
|
|
" if cell.value == \"#N/A\":\r\n",
|
|
" cell.value = None \r\n",
|
|
"\r\n",
|
|
"def finalize(vorlage):\r\n",
|
|
" for pack in [\"Pack4\", \"Pack3\", \"Pack2\", \"Pack1\"]:\r\n",
|
|
" sheet = vorlage[pack]\r\n",
|
|
" vorlage.move_sheet(sheet, -vorlage.index(sheet))\r\n",
|
|
" finalizeSheet(vorlage[pack])\r\n",
|
|
" \r\n",
|
|
" sheet = vorlage[\"Mischungen\"]\r\n",
|
|
" sheet[\"B2\"] = KW\r\n",
|
|
" vorlage.move_sheet(sheet, -vorlage.index(sheet) + 4)\r\n",
|
|
" \r\n",
|
|
" removeUnused(vorlage)\r\n",
|
|
" \r\n",
|
|
"def removeUnused(vorlage):\r\n",
|
|
" vorlage.remove(vorlage[\"Schedule\"])\r\n",
|
|
" vorlage.remove(vorlage[\"1-Schicht\"])\r\n",
|
|
" vorlage.remove(vorlage[\"1-Schicht-WU\"])\r\n",
|
|
" vorlage.remove(vorlage[\"1-Schicht+Mischungen\"])\r\n",
|
|
" vorlage.remove(vorlage[\"2-Schicht\"])\r\n",
|
|
" vorlage.remove(vorlage[\"3-Schicht\"])\r\n",
|
|
" vorlage.remove(vorlage[\"ProduktionStammdaten\"])\r\n",
|
|
" vorlage.remove(vorlage[\"Leistungsdaten\"])\r\n",
|
|
" vorlage.remove(vorlage[\"Artikelstamm\"])\r\n",
|
|
"\r\n",
|
|
"try:\r\n",
|
|
" weekPlan = load_workbook(filename = PLANUNG_PATH, data_only = True, read_only = False)\r\n",
|
|
" finalize(weekPlan)\r\n",
|
|
" weekPlan.save(FINAL_PATH)\r\n",
|
|
"except:\r\n",
|
|
" print(f\"Saving the Excel file {PLANUNG_PATH} failed. Is it still opened?\")\r\n",
|
|
"finally:\r\n",
|
|
" weekPlan.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Create List of WG Brackenheim products"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"from openpyxl.styles import Border, Side\r\n",
|
|
"thin = Side(border_style=\"thin\", color=\"000000\")\r\n",
|
|
"\r\n",
|
|
"productset = [p for p in scheduleP1 if isinstance(p, Produktionsauftrag)]\r\n",
|
|
"productset.extend([p for p in scheduleP2 if isinstance(p, Produktionsauftrag)])\r\n",
|
|
"productset.extend([p for p in scheduleP4 if isinstance(p, Produktionsauftrag)])\r\n",
|
|
"\r\n",
|
|
"FINAL = f\"WG Brackenheim KW{KW}.xlsx\"\r\n",
|
|
"VORLAGE = f\"Dürrenzimmernvorlage.xlsx\"\r\n",
|
|
"vorlage = load_workbook(VORLAGE, data_only=False, read_only = False)\r\n",
|
|
"sheet = vorlage.active\r\n",
|
|
"try:\r\n",
|
|
" row = 3\r\n",
|
|
" for pa in productset:\r\n",
|
|
" prod = prodDict[pa.artikelNummer]\r\n",
|
|
" if prod.pickArea != \"33\":\r\n",
|
|
" continue\r\n",
|
|
" sheet.cell(row = row, column = 1).value = pa.artikelNummer\r\n",
|
|
" sheet.cell(row = row, column = 1).border = Border(bottom=thin, top=thin)\r\n",
|
|
" sheet.cell(row = row, column = 2).value = pa.artikelBezeichnung\r\n",
|
|
" sheet.cell(row = row, column = 2).border = Border(bottom=thin, top=thin)\r\n",
|
|
" sheet.cell(row = row, column = 3).value = prod.bundle\r\n",
|
|
" sheet.cell(row = row, column = 3).border = Border(bottom=thin, top=thin)\r\n",
|
|
" sheet.cell(row = row, column = 4).value = math.ceil(pa.anzGebinde / (prod.palSize * 2 if prod.palSize == 64 else prod.palSize))\r\n",
|
|
" sheet.cell(row = row, column = 4).border = Border(bottom=thin, top=thin)\r\n",
|
|
" row += 1\r\n",
|
|
" vorlage.save(FINAL)\r\n",
|
|
"finally:\r\n",
|
|
" vorlage.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"# Create a checklist for bags"
|
|
],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"PLANUNG_PATH_OLD = f\"Wochenplanung KW {KW-1:02d}_temp.xlsx\"\r\n",
|
|
"scheduleOld = load_workbook(filename = PLANUNG_PATH_OLD, data_only = False, read_only = False)\r\n",
|
|
"try:\r\n",
|
|
" scheduleP4Old = readSchedule(schedule[\"Schedule Pack4\"])\r\n",
|
|
" scheduleP2Old = readSchedule(schedule[\"Schedule Pack2\"])\r\n",
|
|
" scheduleP1Old = readSchedule(schedule[\"Schedule Pack1\"])\r\n",
|
|
"except PermissionError:\r\n",
|
|
" print(f\"Reading the Excel file {PLANUNG_PATH_OLD} failed. Is it still open?\")\r\n",
|
|
"finally:\r\n",
|
|
" schedule.close()\r\n"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"productset = [p.artikelNummer for p in scheduleP1Old if isinstance(p, Produktionsauftrag)]\r\n",
|
|
"productset.extend([p.artikelNummer for p in scheduleP2Old if isinstance(p, Produktionsauftrag)])\r\n",
|
|
"productset.extend([p.artikelNummer for p in scheduleP4Old if isinstance(p, Produktionsauftrag)])\r\n",
|
|
"\r\n",
|
|
"HLB_COUNT_PATH = f\"Zaehlliste KW{KW-1}.xlsx\"\r\n",
|
|
"HLB_VORLAGE_PATH = \"Abruf HLB Vorlage - Lagerplätze.xlsx\"\r\n",
|
|
"vorlage_hlb = load_workbook(HLB_VORLAGE_PATH, data_only=True, read_only = False)\r\n",
|
|
"sheet = vorlage_hlb[\"Tüten HLB\"]\r\n",
|
|
"header = [\"Artikelnr. Tüten\", \"Format\", \"Artikelnr. VP\", \"Bezeichnung\", \"Tütengröße\", \"Menge pro Karton\", \"\", \"Marke\", \"Lagerplatz Reihe\", \"Lagerplatz Position\", \"Bemerkungen\", \"Kartons\", \"Stück\", \"Bestellung Karton\", \"In Stück\"]\r\n",
|
|
"to_count = set()\r\n",
|
|
"try:\r\n",
|
|
" for row in sheet.iter_rows(min_row=2, max_row=len(sheet['A']), max_col=15, values_only=True):\r\n",
|
|
" artNr = row[2]\r\n",
|
|
" if artNr in productset:\r\n",
|
|
" to_count.add(row)\r\n",
|
|
"finally:\r\n",
|
|
" vorlage_hlb.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [
|
|
"hlb_count = Workbook()\r\n",
|
|
"sheet = hlb_count.active\r\n",
|
|
"sheet.title = \"HLB Tüten\"\r\n",
|
|
"try:\r\n",
|
|
" sheet.append((header))\r\n",
|
|
" to_count_list = list()\r\n",
|
|
" to_count_list.extend(to_count)\r\n",
|
|
" to_count_list.sort(key = lambda i: i[2])\r\n",
|
|
" for row in to_count_list:\r\n",
|
|
" sheet.append(row)\r\n",
|
|
" \r\n",
|
|
" tab = Table(displayName=\"Counting\", ref=f\"A1:O{len(to_count_list)+2}\")\r\n",
|
|
"\r\n",
|
|
" sheet.add_table(tab)\r\n",
|
|
" hlb_count.save(HLB_COUNT_PATH)\r\n",
|
|
"finally:\r\n",
|
|
" hlb_count.close()"
|
|
],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"source": [],
|
|
"outputs": [],
|
|
"metadata": {}
|
|
}
|
|
],
|
|
"metadata": {
|
|
"interpreter": {
|
|
"hash": "032eb40c1682b1e6109824d577ff4427b0bbc2f8ebe1487b7b1be524c4843266"
|
|
},
|
|
"kernelspec": {
|
|
"name": "python3",
|
|
"display_name": "Python 3.9.7 64-bit"
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.9.7",
|
|
"mimetype": "text/x-python",
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"pygments_lexer": "ipython3",
|
|
"nbconvert_exporter": "python",
|
|
"file_extension": ".py"
|
|
},
|
|
"metadata": {
|
|
"interpreter": {
|
|
"hash": "032eb40c1682b1e6109824d577ff4427b0bbc2f8ebe1487b7b1be524c4843266"
|
|
}
|
|
},
|
|
"orig_nbformat": 2
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
} |