class GFS(MDApp): def __init__(self, **kwargs): super().__init__(**kwargs) if PROD: self.db = Database(CONFIG) ###Load the kv file with the encoding utf8 [!important in winidows] with open('main.kv', encoding='utf-8') as f: self.INTERFACE = Builder.load_string(f.read()) ###Application color_theme parameter in general self.theme_cls.primary_palette = "Red" self.theme_cls.primary_hue = "A700" self.theme_cls.theme_style = "Light" self.quit_dialog = None self.dialog = None self.db = None self.__tableau() self.__tableau_status() self.__tableau_status_facture() self.transact = [] self.__tableau_transaction(self.transact) ###For the MDRaisedButton in Grand-menage for i in range(10): self.INTERFACE.ids[ f'raisedBtn{i+1}'].md_bg_color = get_color_from_hex("#2763e1") ###The dropdown menu in the profile button in the top-right menu_items = [{ "icon": "account", "text": "Profil" }, { "icon": "logout", "text": "Déconnexion" }, { "icon": "exit-run", "text": "Quit" }] self.menu = MDDropdownMenu( caller=self.INTERFACE.ids.button_2, items=menu_items, width_mult=5, ) self.menu.bind(on_release=self.logout) self.date_dialog = MDDatePicker( callback=self.get_date, year=2010, month=2, day=12, ) ###Instance of the Database class def on_start(self): if PROD: self.db = Database(CONFIG) def build(self): return self.INTERFACE #################################################### ## About login # #################################################### def loadConnection(self): if not self.db and PROD: self.INTERFACE.ids.errorLogin.text = "Vérifier votre connexion Internet" return self.INTERFACE.ids.button_login.text = " " self.INTERFACE.ids.button_login.icon = " " self.INTERFACE.ids.loader.color = (1, 1, 1, 1) self.INTERFACE.ids.loader.active = True Clock.schedule_once(self.login, 1) def initialisaion(self): self.INTERFACE.ids.loader.active = False self.INTERFACE.ids.password.text = "" self.INTERFACE.ids.button_login.icon = "arrow-right" self.INTERFACE.ids.button_login.text = "S'identifier" def login(self, event): username = self.INTERFACE.ids.username.text password = self.INTERFACE.ids.password.text if not PROD or self.db.login(username, password): self.INTERFACE.current = "Main" self.INTERFACE.ids.username.text = "" self.INTERFACE.ids.errorLogin.text self.initialisaion() return self.initialisaion() self.INTERFACE.ids.errorLogin.text = "Le mot de passe est incorrect !" #################################################### ## Logout || Quit the app # #################################################### def logout(self, menu, item): if item.text == "Déconnexion": _SESSION = None self.INTERFACE.current = "Login" self.menu.dismiss() if item.text == "Quit": self.show_quit_dialog() def show_quit_dialog(self): if not self.quit_dialog: self.quit_dialog = MDDialog( text="Etes vous sur de quittez ?", buttons=[ MDFlatButton(text="ANNULER", text_color=self.theme_cls.primary_color, on_press=self.close_quit_Dialog), MDFlatButton(text="CONFIRMER", text_color=self.theme_cls.primary_color, on_press=self.close_quit_Dialog), ], ) self.quit_dialog.bind(on_release=self.close_quit_Dialog) self.quit_dialog.open() def close_quit_Dialog(self, btn): if btn.text == "ANNULER": self.quit_dialog.dismiss() else: self.stop() #################################################### ## Tour de tâche # #################################################### def tour_de_tache(self, jour, tache, slide): date_tache = datetime.date.today() if jour == "hier": if slide == 1: date_tache = datetime.date.today() - timedelta(1) elif slide == 2: date_tache = datetime.date.today() + timedelta(2) elif jour == "aujourdhui": if slide == 1: date_tache = datetime.date.today() elif slide == 2: date_tache = datetime.date.today() + timedelta(3) elif jour == "demain": if slide == 1: date_tache = datetime.date.today() + timedelta(1) elif slide == 2: date_tache = datetime.date.today() + timedelta(4) date_tache = date_tache.strftime("%Y-%m-%d") return self.db.tour_tache(date_tache, tache) def jour_de_tache(self, jour, slide): date_tache = datetime.date.today() if jour == "hier": if slide == 1: date_tache = datetime.date.today() - timedelta(1) elif slide == 2: date_tache = datetime.date.today() + timedelta(2) elif jour == "aujourdhui": if slide == 1: date_tache = datetime.date.today() elif slide == 2: date_tache = datetime.date.today() + timedelta(3) elif jour == "demain": if slide == 1: date_tache = datetime.date.today() + timedelta(1) elif slide == 2: date_tache = datetime.date.today() + timedelta(4) return date_tache.strftime("%d-%m-%y") #################################################### ## Tableau in the cook menu # #################################################### def __tableau(self): tab = MDDataTable( size_hint=(0.9, 0.3), column_data=[ ("Lundi", dp(24)), ("Mardi", dp(24)), ("Mercredi", dp(24)), ("Jeudi", dp(24)), ("Vendredi", dp(24)), ("Samedi", dp(24)), ("Dimanche", dp(24)), ], row_data=[( "Oeuf sauce", "Kitoza", "Voanjobory", "Soupe Légume", "Poulet frite", "Tsaramaso", "Brède", ), ( "4 500 Ar", "2 500 Ar", "5 000 Ar", "6 000 Ar", "2 000 Ar", "3 500 Ar", "3 000 Ar", )], ) self.INTERFACE.ids.tableau_repas.add_widget(tab) #################################################### ## Tableau de status de cotisation # #################################################### def __tableau_status(self): tab = MDDataTable( size_hint=(0.815, 0.45), column_data=[ ("Membres", dp(50)), ("Cotisations", dp(50)), ("Status", dp(50)), ], row_data=[ # The number of elements must match the length # of the `column_data` list. ( "Landry", "Bazar", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Gaetan", "Tee-shirt", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Haja", "Tee-shirt", ( "checkbox-marked-circle", [39 / 256, 174 / 256, 96 / 256, 1], "Payé", ), ), ( "Fabien", "Bazar", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Casmir", "Tee-shirt", ( "checkbox-marked-circle", [39 / 256, 174 / 256, 96 / 256, 1], "Payé", ), ), ], ) self.INTERFACE.ids.tableau_status_cotisation.add_widget(tab) #################################################### ## Tableau de status de cotisation # #################################################### def __tableau_status_facture(self): tab = MDDataTable( size_hint=(0.815, 0.45), column_data=[ ("Membres", dp(50)), ("Factures", dp(50)), ("Status", dp(50)), ], row_data=[ # The number of elements must match the length # of the `column_data` list. ( "Landry", "Septembre", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Gaetan", "Février", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Haja", "Novembre", ( "checkbox-marked-circle", [39 / 256, 174 / 256, 96 / 256, 1], "Payé", ), ), ( "Fabien", "Janvier", ("alert", [255 / 256, 165 / 256, 0, 1], "En attente"), ), ( "Casmir", "Décembre", ( "checkbox-marked-circle", [39 / 256, 174 / 256, 96 / 256, 1], "Payé", ), ), ], ) self.INTERFACE.ids.tableau_status_facture.add_widget(tab) #################################################### ## Tableau de transaction # #################################################### def __tableau_transaction(self, transact): self.tab = MDDataTable( size_hint=(0.813, 0.45), column_data=[ ("Dates de transaction", dp(35)), ("Dates d'insertion", dp(30)), ("Membres", dp(25)), ("Montants", dp(25)), ("Raisons", dp(40)), ], row_data=transact, ) self.INTERFACE.ids.tableau_transaction.clear_widgets() self.INTERFACE.ids.tableau_transaction.add_widget(self.tab) #################################################### ## Grand Menage # #################################################### ###Reinitialiser les tâches def show_init_dialog(self, num): self.num = num self.dialog = None if not self.dialog: self.dialog = MDDialog( title="Réinitialisation ?", text="Voulez-vous vraiment réinitialiser cette tâche ?", buttons=[ MDFlatButton(text="ANNULER", text_color=self.theme_cls.primary_color, on_press=self.close_init_Dialog), MDFlatButton(text="VALIDER", text_color=self.theme_cls.primary_color, on_press=self.close_init_Dialog), ], ) self.dialog.open() def close_init_Dialog(self, btn): if btn.text == "ANNULER": self.dialog.dismiss() if btn.text == "VALIDER": self.INTERFACE.ids[f'raisedBtn{self.num}'].text = "" self.dialog.dismiss() ###Modifier les tâches def show_edit_dialog(self, num): self.num = num self.dialog = None if not self.dialog: cls = BoxLayout(orientation="vertical", spacing="12dp", size_hint_y=None, height="120dp") cls.add_widget( MDLabel( text=self.INTERFACE.ids[f'raisedBtn{self.num-1}'].text)) self.personne = MDTextField( text=self.INTERFACE.ids[f'raisedBtn{self.num}'].text, ) self.dialog = MDDialog( title="Grand Menage:", type="custom", content_cls=cls, buttons=[ MDFlatButton(text="ANNULER", text_color=self.theme_cls.primary_color, on_press=self.close_edit_Dialog), MDFlatButton(text="OK", text_color=self.theme_cls.primary_color, on_press=self.close_edit_Dialog), ], ) cls.add_widget(self.personne) self.dialog.open() def close_edit_Dialog(self, btn): if btn.text == "ANNULER": self.dialog.dismiss() if btn.text == "OK": self.INTERFACE.ids[ f'raisedBtn{self.num}'].text = self.personne.text self.dialog.dismiss() #################################################### ## Cotisation # #################################################### ###Add cotisation def add_cotis_dialog(self): self.dialog = None if not self.dialog: cls = BoxLayout(orientation="vertical", spacing="12dp", size_hint_y=None, height="210dp") ###Reason of the cotisation cls.add_widget(MDLabel(text="Motifs")) self.motifs_cotis = MDTextField() cls.add_widget(self.motifs_cotis) ###Amount of the cotisation cls.add_widget(MDLabel(text="Montant")) self.argent_cotis = MDTextField() cls.add_widget(self.argent_cotis) ###The date of the transaction cls.add_widget(MDLabel(text="Date")) self.date_cotis = MDTextField(on_release=self.show_date_picker) cls.add_widget(self.date_cotis) self.dialog = MDDialog( title="Ajouter cotisation:", type="custom", content_cls=cls, buttons=[ MDFlatButton(text="ANNULER", text_color=self.theme_cls.primary_color, on_press=self.close_cotis_Dialog), MDFlatButton(text="OK", text_color=self.theme_cls.primary_color, on_press=self.close_cotis_Dialog), ], ) self.dialog.open() self.date_cotis.bind(focus=lambda *args: self.show_date_picker()) def close_cotis_Dialog(self, btn): if btn.text == "OK": new_card = MDCard(orientation="vertical", padding="15dp", size_hint=(None, None), size=("180dp", "220dp"), pos_hint={ "center_x": 0.80, "center_y": 0.31 }) new_card.add_widget( MDFloatingActionButton( icon="alert", user_font_size="14sp", theme_text_color="Custom", text_color=get_color_from_hex("#ffffff"), md_bg_color=get_color_from_hex("#faaf00"), elevation_normal=0)) new_card.add_widget( MDLabel(text=self.motifs_cotis.text, pos_hint={ "x": 0.30, "y": 0.75 }, font_size='18sp')) new_card.add_widget( MDRoundFlatIconButton(icon="currency-eur", text=self.argent_cotis.text, pos_hint={ "center_x": 0.5, "center_y": 0.5 }, font_size='17sp', margin="30dp")) new_card.add_widget( MDIconButton(icon="trash-can", theme_text_color="Custom", font_size="18sp", text_color=get_color_from_hex("#071f38"), pos_hint={ "center_x": 0.5, "center_y": 0.3 })) self.INTERFACE.ids.Cotisation.add_widget(new_card) self.dialog.dismiss() ###Payer cotisation def paye_cotisation(self): self.dialog = None if not self.dialog: cls = BoxLayout(orientation="vertical", spacing="12dp", size_hint_y=None, height="300dp") ###Reason of the payement cls.add_widget(MDLabel(text="Motifs")) self.motifs_paye = MDTextField() cls.add_widget(self.motifs_paye) ###Who did the payement cls.add_widget(MDLabel(text="Prenom")) self.who_paye = MDTextField() cls.add_widget(self.who_paye) ###Amount of the payement cls.add_widget(MDLabel(text="Montant")) self.argent_paye = MDTextField() cls.add_widget(self.argent_paye) ###The date of the transaction cls.add_widget(MDLabel(text="Date de payement")) self.date_paye = MDTextField() cls.add_widget(self.date_paye) self.dialog = MDDialog( title="Payement de cotisation:", type="custom", content_cls=cls, buttons=[ MDFlatButton(text="ANNULER", text_color=self.theme_cls.primary_color, on_press=self.close_paye_Dialog), MDFlatButton(text="OK", text_color=self.theme_cls.primary_color, on_press=self.close_paye_Dialog), ], ) self.dialog.open() def close_paye_Dialog(self, btn): if btn.text == "OK": self.transact.append((self.date_paye.text, datetime.date.today().strftime("%d-%m-%Y"), self.who_paye.text, self.argent_paye.text, self.motifs_paye.text)) self.__tableau_transaction(self.transact) self.dialog.dismiss() ###Show DatePicker def get_date(self, date): ''' :type date: <class 'datetime.date'> ''' def show_date_picker(self): date_dialog = MDDatePicker(callback=self.get_date) date_dialog.open()
def gotoStreamRow(self, stream, postID, document=None, noBack=False, template=None): "Editor/viewer for ONE specific row" self.streamEditPanel.clear_widgets() self.streamEditPanel.add_widget( MDToolbar(title="Table Row in " + stream)) self.streamEditPanel.add_widget(self.makeBackButton()) if not noBack: def goHere(): self.gotoStreamRow(stream, postID) self.backStack.append(goHere) self.backStack = self.backStack[-50:] document = document or daemonconfig.userDatabases[ stream].getDocumentByID(postID, allowOrphans=True) if 'type' in document and not document['type'] == 'row': raise RuntimeError("Document is not a row") document['type'] = 'row' title = Label(text=document.get("name", ''), font_size='22sp') #Our default template if none exists #Give it a name because eventually we may want to have multiple templates. #Give it an ID so it can override any existing children of that template. #Use only the direct ID of the parent record in cade we want to move it eventually. oldTemplate = { 'type': "row.template", 'leafNode': True, 'parent': document['parent'], 'name': 'default', 'id': uuid.uuid5(uuid.UUID(document['parent'].split("/")[-1]), ".rowtemplate.default") } for i in daemonconfig.userDatabases[stream].getDocumentsByType( "row.template", parent=document['parent'], limit=1, allowOrphans=True): oldTemplate = i template = template or oldTemplate def post(*a): with daemonconfig.userDatabases[stream]: #Make sure system knows this is not an old document try: del document['time'] except: pass daemonconfig.userDatabases[stream].setDocument(document) #If the template has changed, that is how we know we need to save template changes at the same time as data changes if not template.get('time', 0) == oldTemplate.get('time', 1): daemonconfig.userDatabases[stream].setDocument(template) daemonconfig.userDatabases[stream].commit() self.unsavedDataCallback = None self.goBack() btn1 = Button(text='Save Changes') btn1.bind(on_release=post) self.streamEditPanel.add_widget(title) buttons = BoxLayout(orientation="horizontal", spacing=10, adaptive_height=True) if daemonconfig.userDatabases[stream].writePassword: self.streamEditPanel.add_widget(buttons) buttons.add_widget(btn1) def delete(*a): def reallyDelete(v): if v == postID: with daemonconfig.userDatabases[stream]: daemonconfig.userDatabases[stream].setDocument({ 'type': 'null', 'id': postID }) daemonconfig.userDatabases[stream].commit() self.gotoStreamPosts(stream) self.askQuestion("Delete table row permanently on all nodes?", postID, reallyDelete) btn1 = Button(text='Delete') btn1.bind(on_release=delete) if daemonconfig.userDatabases[stream].writePassword: buttons.add_widget(btn1) names = {} self.streamEditPanel.add_widget(MDToolbar(title="Data Columns:")) for i in template: if i.startswith('row.'): names[i] = '' for i in document: if i.startswith('row.'): if i in template: names[i] = '' else: #In the document but not the template, it is an old/obsolete column, show that to user. names[i] = '(removed)' for i in names: self.streamEditPanel.add_widget(Button(text=i[4:])) d = document.get(i, '') try: d = float(d) except: pass x = MDTextField(text=str(d) + names[i], mode='fill', multiline=False, font_size='22sp') def oc(*a, i=i, x=x): d = x.text.strip() if isinstance(d, str): d = d.strip() try: d = float(d or 0) except: pass document[i] = d x.bind(text=oc) self.streamEditPanel.add_widget(x) if isinstance(d, float) or not d.strip(): l = BoxLayout(orientation="horizontal", spacing=10, adaptive_height=True) b = MDRoundFlatButton(text="--") def f(*a, i=i, x=x): d = document.get(i, '') if isinstance(d, str): d = d.strip() try: d = float(d or 0) except: return document[i] = d - 1 x.text = str(d - 1) b.bind(on_release=f) b2 = MDRoundFlatButton(text="++") def f(*a, i=i, x=x): d = document.get(i, '') if isinstance(d, str): d = d.strip() try: d = float(d or 0) except: return document[i] = d + 1 x.text = str(document[i]) b2.bind(on_release=f) l.add_widget(b) l.add_widget(b2) self.streamEditPanel.add_widget(l) b = MDRoundFlatButton(text="Add Column") def f(*a): def f2(r): if r: template['row.' + r] = '' #Remove time field which marks it as a new record that will get a new timestamp rather than #being ignored when we go to save it, for being old. template.pop('time', None) #Redraw the whole page, it is lightweight, no DB operation needed. self.gotoStreamRow(stream, postID, document=document, noBack=True, template=template) self.askQuestion("Name of new column?", cb=f2) b.bind(on_release=f) self.streamEditPanel.add_widget(b) b = MDRoundFlatButton(text="Del Column") def f(*a): def f2(r): if r: try: del template['row.' + r] template.pop('time', None) except: pass #Redraw the whole page, it is lightweight, no DB operation needed. self.gotoStreamRow(stream, postID, document=document, noBack=True, template=template) self.askQuestion("Column to delete?", cb=f2) b.bind(on_release=f) self.streamEditPanel.add_widget(b) self.screenManager.current = "EditStream"