Exemplo n.º 1
0
class Main(Frame):
    """
    Main class of the system.
    """

    def __init__(self, master=None):
        """
        Main contstructor. Creates an instance of :class:`Storage<storage.Storage>` and starts the graphics-window.
        """
        args = {
            "fields": ["name", "date", "lifetime"],
            "ident": "name",
            "uniqueident": True,
            "objectname": "person",
            "gui": self,
        }
        self.storage = Storage(**args)
        self.is_clicked = False
        self.clicked_id = -1
        Frame.__init__(self, master)
        self.master.title("Medlemsregister Cybernetisk Selskab")
        self.grid(padx=15, pady=15)

        if not self.storage._testfile():
            path = os.path.abspath(__file__).rsplit("/", 1)[0]
            self._popup(
                "ERROR!",
                'Cannot write to file! make sure folder "{}/medlemslister" exists, then restart..'.format(path),
                "error",
            )
            self.destroy()
            self.master.destroy()
            return

        self.create_elements()

        loadmsg = self.storage.load()
        if "error" in loadmsg:
            self.infotext.set("ERROR! {}".format(loadmsg["error"]))
        elif "success" in loadmsg:
            self.infotext.set("Success! {}".format(loadmsg["success"]))

        self._populate_list()

    def create_elements(self):
        """
        creates all graphics elements and places them in the graphics grid.
        """

        # global shortcuts
        self.master.bind("<F1>", self.display_help)
        self.master.bind("<Control-f>", self.search)
        self.master.bind("<Control-d>", self.delete)
        self.master.bind("<Control-r>", self._populate_list)
        self.master.bind("<Control-s>", self.save_to_file)

        monospace = tkFont.Font(family="Courier", size=10, weight="normal")

        # menubar:
        menubar = Menu(self.master)

        backupmenu = Menu(menubar, tearoff=0)
        backupmenu.add_command(label="Backup to Google (Note: Slow!)", command=self.google_write)
        backupmenu.add_command(label="Read backup from Google", command=self.google_read)
        backupmenu.add_separator()
        backupmenu.add_command(label="Merge with Wiki", command=self.wiki_merge)
        backupmenu.add_command(label="Overwrite Wiki with local storage", command=self.wiki_overwrite)

        specialmenu = Menu(menubar, tearoff=0)
        specialmenu.add_command(label="Set as lifetime member", command=self.set_lifetime)
        specialmenu.add_command(label="Remove lifetime membership", command=self.unset_lifetime)
        specialmenu.add_command(label="Change a users id", command=self.update_id)

        menubar.add_cascade(label="Backup", menu=backupmenu)
        menubar.add_cascade(label="Special Actions", menu=specialmenu)
        menubar.add_command(label="Help (F1)", command=self.display_help)

        self.master.config(menu=menubar)

        # Info-label
        self.infotext = StringVar()
        self.info = Label(self, textvariable=self.infotext)
        self.info.pack()
        self.info.grid(row=0, column=0, columnspan=10)
        self.infotext.set("Welcome")

        # Save-button
        self.savebtn = Button(self, text="Save (enter)", command=self.create, width=11)
        self.savebtn.grid(row=3, column=7)

        # Omnibar (entry-field for add/search/delete)
        self.omnibar = Entry(self, width=28)
        self.omnibar.grid(row=3, column=0, columnspan=1)
        self.omnibar.bind("<Return>", self.create)
        self.omnibar.configure(font=monospace)

        # List of members
        self.memlist = SL(self, height=25, width=71, callback=self._click_list)
        self.memlist.grid(row=7, column=0, columnspan=10)
        self.memlist.listbox.configure(font=monospace)

        # Search-button
        self.searchbtn = Button(self, text="Search (ctrl-f)", command=self.search, width=11)
        self.searchbtn.grid(row=3, column=8)

        self.searchlist = False

        # Delete-button
        self.delete_btn = Button(self, text="Delete (ctrl-d)", command=self.delete, width=11)
        self.delete_btn.grid(row=3, column=9)

        # Counter
        self.count = StringVar()
        self.countlbl = Label(self, textvariable=self.count)
        self.countlbl.pack()
        self.countlbl.grid(row=8, column=0, sticky="W")

        # Reset list-button
        self.refreshbtn = Button(self, text="Refresh list (ctrl-r)", command=self._populate_list, width=12)
        self.refreshbtn.grid(row=8, column=9)

        # Save to file-button
        self.saveallbtn = Button(self, text="Save to file (ctrl-s)", command=self.save_to_file, width=12)
        self.saveallbtn.grid(row=8, column=8)

        # Help-button
        # self.helpbutton=Button(self, text='Help', command=self.display_help, width=11)
        # self.helpbutton.grid(row=8, column=7)

    def display_help(self, event=None):
        """
        Display a new window with help-text from file 'help.txt'
        """
        help = Toplevel()
        help.title("Help and usage")
        path = os.path.abspath(__file__).rsplit("/", 1)[0]
        f = open(u"{}/help.txt".format(path), "r")
        helptext = f.read()
        f.close
        helplabel = Label(help, text=helptext, relief=RIDGE, padx=15, pady=15, anchor="w", justify=LEFT, bg="white")
        helplabel.pack(side=TOP, fill=BOTH, expand=YES)

    def create(self, event=None):
        """
        Called when a user clicks the 'save person'-button or presses enter while typing in the name-field.
        Sets the info-label with feedback from :class:`Storage<storage.Storage>`

        :param event: the button-event of the enter-key.
        """
        if self.searchlist:
            self._populate_list()
            self.searchlist = False

        name = self._get_val()
        date = dt.now().strftime("%Y-%m-%d %H:%M")

        obj = self.storage.create(**{"name": name, "date": date, "lifetime": "n"})
        if not "success" in obj:
            self.infotext.set(u"FAILURE! {}".format(obj["error"]))
        else:
            self.infotext.set(u"Success! User added with id: {}".format(obj["success"]))
            self._list_add(obj["success"], obj["object"]["name"], obj["object"]["date"])

        self._update_count()

    def search(self, event=None):
        """
        Locate all people matching search-parameters in search-fields.

        called when user clicks search or when user presses enter while cursor is in one of the search-fields.

        :param event: button-event for enter-click.
        """
        self.infotext.set("")
        text = self._get_val()

        name = ""
        date = ""

        if len(text.split("-")) > 1 or len(text.split(":")) == 2:
            date = text
        else:
            name = text

        args = {"name": name, "date": date}

        obj = self.storage.search(**args)
        self.searchlist = True
        self.memlist.clear()
        i = 0
        l = 0
        for k, v in obj.iteritems():
            self._list_add(k, v["name"], v["date"])
            if not "L" in "{}".format(k):
                i += 1
            else:
                l += 1

        self._update_count(u"Life: {} Normal: {}".format(l, i))

    def delete(self, event=None):
        """
        Delete a person from collection based on `id`.

        called when user clicks delete or when user presses enter while cursor is in the delete-field.
        """
        val = self._get_val()

        if val == "":
            if self.is_clicked:
                id = self.clicked_id
                self.is_clicked = False
                self.clicked_id = -1
            else:
                self.infotext.set(u"FAILURE! No id provided. Either click a person in the list or write an id.")
                return
        elif "L" in val:
            id = val
        else:
            id = int(val)

        obj = self.storage.read(**{"id": id})
        if "success" in obj:
            check = self._popup(
                u"Really delete?",
                u"Do you really want to delete '{}'?".format(obj["success"]["name"].title()),
                "warning",
            )
            if not check:
                self.infotext.set(u"Delete aborted..")
                return

        obj = self.storage.delete(**{"id": id})
        if "success" in obj:
            self.infotext.set(u"Success! {}".format(obj["success"]))
            self._populate_list()
        else:
            self.infotext.set(u"FAILURE! {}".format(obj["error"]))
        self._update_count()

    def _list_add(self, id, name, date):
        """
        adds a person with id, name and timestamp to the list of users in the ui.

        :param id: id of a person.
        :param name: name of a person.
        :param date: date of a person.
        """
        self.memlist.append(u" {:<5} - {:40} - {}".format(id, name.title(), date))

    def _populate_list(self, event=None):
        """
        Refreshes the list of users in the ui.
        """
        self.memlist.clear()
        sorted_keys = sorted(self.storage.storage.iterkeys())
        for k in sorted_keys:
            v = self.storage.storage[k]
            self._list_add(k, v["name"], v["date"])
        self._update_count()

    def _update_count(self, count=None):
        """
        Updates the counter in the ui with number from :func:`storage.size()<storage.Storage.size>`
        """
        if count:
            self.count.set(u"{}".format(count))
        else:
            self.count.set(u"Total: {}".format(self.storage.size()))

    def _click_list(self, linenum):
        """
        called when a user clicks a member in the list of members. Saves the id of the user.

        :param linenum: the linenumber the user clicked.
        """
        line = ""
        try:
            line = self.memlist[linenum]
        except (IndexError):
            return
        self._get_val()
        vals = line.split("-")
        if not "L" in vals[0]:
            self.clicked_id = int(vals[0].strip())
        else:
            self.clicked_id = vals[0].strip()
        self.is_clicked = True

    def google_write(self):
        """
        backup collection to a google spreadsheet
        """
        obj = self.storage.google_write()
        if "success" in obj:
            self.infotext.set(u"Success! collection backed up to google spreadsheet.")
        else:
            self.infotext.set(u"Failure! {}".format(obj["error"]))

    def google_read(self):
        """
        read backup of collection from a google spreadsheet
        """
        obj = self.storage.google_read()
        if "success" in obj:
            self.infotext.set(u"Success! {}".format(obj["success"]))
            self._populate_list()
        else:
            self.infotext.set(u"Failure! {}".format(obj["error"]))

    def wiki_merge(self):
        """
        Merge local collection with wiki
        """
        self.infotext.set(u"Please Wait...")
        val = self.storage.wiki_merge()
        val = self.storage.wiki_merge(lifetime=True)
        self.infotext.set(val["status"])

    def wiki_overwrite(self):
        """
        overwrite wiki with data from local storage
        """
        self.infotext.set(u"Please Wait...")
        val = self.storage.wiki_merge(overwrite_wiki=True, push_to_wiki=True)
        val = self.storage.wiki_merge(overwrite_wiki=True, push_to_wiki=True, lifetime=True)
        self.infotext.set(val["status"])

    def set_lifetime(self):
        """
        register lifetime membership for a user. The user is selected by clicking in the list.
        """
        if not self.is_clicked:
            self.infotext.set(u"FAILURE! No id provided. You have to click a person in the list!.")
            return

        id = self.clicked_id
        self.is_clicked = False
        self.clicked_id = -1

        obj = self.storage.read(**{"id": id})
        if "success" in obj:
            check = self._popup(
                u"Set lifetime membership?",
                u"Do you really want to give '{}' a lifetime membership?".format(obj["success"]["name"].title()),
                "warning",
            )
            if not check:
                self.infotext.set(u"{} does NOT have a lifetime membership..".format(obj["success"]["name"].title()))
                return

        obj = self.storage.update(**{"id": id, "life": True})
        if "error" in obj:
            self.infotext.set(u"FAILURE! {}".format(obj["error"]))
        elif "success" in obj:
            self.infotext.set(u"Success! {}".format(obj["success"]))
            self._populate_list()

    def unset_lifetime(self):
        """
        remove a lifetime membership from a user. The user is selected by clicking in the list.
        """
        if not self.is_clicked:
            self.infotext.set(u"FAILURE! No id provided. You have to click a person in the list!.")
            return

        id = self.clicked_id
        self.is_clicked = False
        self.clicked_id = -1

        obj = self.storage.read(**{"id": id})
        if "success" in obj:
            check = self._popup(
                u"Remove lifetime membership?",
                u"Do you really want to remove '{}'s' lifetime membership?".format(obj["success"]["name"].title()),
                "warning",
            )
            if not check:
                self.infotext.set(u"{} is still a lifetime member.".format(obj["success"]["name"].title()))
                return

        obj = self.storage.update(**{"id": id, "life": False})
        if "error" in obj:
            self.infotext.set(u"FAILURE! {}".format(obj["error"]))
        elif "success" in obj:
            self.infotext.set(u"Success! {}".format(obj["success"]))
            self._populate_list()

    def update_id(self):
        """
        Update the id of a user
        """
        if not self.is_clicked:
            self.infotext.set(u"FAILURE: You have to click a user in the list.")
            return

        newid = self._get_val()
        if newid == "":
            self.infotext.set(u"FAILURE: You have to enter a new id in the textfield.")
            return

        id = self.clicked_id
        self.is_clicked = False
        self.clicked_id = -1

        name = self.storage.read(**{"id": id})
        if not "success" in name:
            self.infotext.set(u"FAILURE: id could not be found..")
            return

        name = u"{}".format(name["success"]["name"].title())
        if not self._popup(u"Change id?", u"Do you really want to change the id of '{}'?".format(name)):
            self.infotext.set(u"Aborted changing id.")
            return

        retval = self.storage.update_id(id, newid)

        self.infotext.set(retval["status"])

        self._populate_list()

    def _get_val(self):
        """
        clears the input-field and returns value that was there.
        """
        val = self.omnibar.get()
        self.omnibar.delete(0, END)
        val = u"{}".format(val)
        return val.lower()

    def save_to_file(self, event=None):
        val = self.storage.save()

        self.infotext.set("{}".format(val["msg"]))

    def _popup(self, title, text, style=None):
        """
        create a popup!

        :param title: title of the popup-window
        :param text: the text in the popup-window
        :param style: the icon-style of the popup. default is 'warning'
        """
        if style:
            if style == "error":
                return tkMessageBox.showerror(title, text)
            return tkMessageBox.askokcancel(title, text, icon=style)
        return tkMessageBox.askokcancel(title, text)
Exemplo n.º 2
0
class Eris:
    def __init__(self):
        self.output = os.path.join(config.logsDir, "output.log")

    def start(self):
        try:
            with open(config.statusFile, "r") as f:
                if f.read() == config.STATUS_LINE:
                    return
        except:
            pass
        try:
            with open(config.statusFile, "w") as f:
                f.write(config.STATUS_LINE)
        except IOError as e:
            print >>sys.stderr, e
            return

        if self.daemonize():
            return

        self.startTime = datetime.now()
        self.storage = Storage()
        self.btserver = BtServer(self.storage)
        self.retriever = Retriever(self.storage)
        self.btserver.start()
        self.retriever.start()

        daemon = Pyro4.Daemon(port=config.pyroPort)
        uri = daemon.register(self, config.PNAME)
        log.info("Eris daemon URI: [{}]".format(uri))
        self.running = True
        daemon.requestLoop(loopCondition=lambda: self.running)

        daemon.unregister(config.PNAME)
        daemon.close()

    def stop(self):
        log.info("Closing eris")
        try:
            self.btserver.kill()
            self.retriever.kill()
            self.retriever.join(1.0)
            self.btserver.join(1.0)

            with open(config.statusFile, "w"):
                pass
        except:
            log.exception("Something went wrong while shutting down")
        self.running = False

    def status(self):
        pid = os.getpid()
        proc = psutil.Process(pid)
        cpu = proc.get_cpu_percent()
        mem = proc.get_memory_percent()
        uptime = datetime.now() - self.startTime
        du = self.storage.size()
        return (pid, cpu, mem, uptime, du)

    def put(self, packets):
        self.storage.put(packets)

    def get(self, since=0, to=0, limit=0):
        connId, _ = self.storage.get(since, to, limit)
        return self.storage.fetchall(connId)

    def count(self):
        return self.storage.rowcount()

    def ping(self):
        return config.PNAME

    def daemonize(self):
        try:
            pid = os.fork()
            if pid > 0:
                return True
        except OSError as e:
            sys.stderr.write("Fork #1 failed: {} ({})\n".format(e.errno, e.strerror))
            sys.exit(1)

        os.chdir("/")
        os.setsid()

        try:
            pid = os.fork()
            if pid > 0:
                sys.exit(0)
        except OSError as e:
            sys.stderr.write("Fork #2 failed: {} ({})\n".format(e.errno, e.strerror))
            sys.exit(1)

        sys.stdout.flush()
        sys.stderr.flush()
        with open(self.output, "w"):
            pass
        out = file(self.output, "a+", 1)
        os.dup2(out.fileno(), sys.stdout.fileno())
        os.dup2(out.fileno(), sys.stderr.fileno())
        return False

    @staticmethod
    def getProxy():
        uri = "PYRO:{}@localhost:{}".format(config.PNAME, config.pyroPort)
        return Pyro4.Proxy(uri)