Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Thykof committed Feb 25, 2017
2 parents c46ff95 + c6451f2 commit cc45a9c
Show file tree
Hide file tree
Showing 20 changed files with 593 additions and 102 deletions.
11 changes: 4 additions & 7 deletions README.md
@@ -1,31 +1,28 @@
# Safe My work
SafeMyWork save all files in the given directory in another directory to keep your work safe and avoid losing data.
SafeMyWork save all files in the given directory in another directory to keep your work safe and avoid loosing data.

[![Documentation Status](https://readthedocs.org/projects/safemywork/badge/?version=master)](http://safemywork.readthedocs.org/en/master)
[![Build Status](https://travis-ci.org/Thykof/SafeMyWork.svg?branch=master)](https://travis-ci.org/Thykof/SafeMyWork)
[![Code Health](https://landscape.io/github/Thykof/SafeMyWork/master/landscape.svg?style=flat)](https://landscape.io/github/Thykof/SafeMyWork/master)

## Version
Current version is 0.2.
Current version is 1.0
#### Current state
- feature/outline
- watch different directories
- specify files and extensions to exclude
- specify directories, files and extensions to exclude
- interface (gtk)

Work in progress...

#### TODO
- make an history of each files
- compress files
- interface (gtk)

## What for ?
SafeMyWork is intend for people who handle lot of files which can be texts, images, songs.

Run SafeMyWork and give it a directory to watch. Then while you are working, every ten minutes all files in this directory will be copy in another directory. As the result of keeping them safe if you does not save your work or delete accidentally files.

## How to use it ?
Run `python3 main.py <path-to-your-dir>` to start. The given path is the directory watching. You don't need extra requirement.

## License
SafeMyWork is under the GNU GPL v3 license.
4 changes: 2 additions & 2 deletions docs/conf.py
Expand Up @@ -59,9 +59,9 @@
# built documents.
#
# The short X.Y version.
version = '0.2'
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '0.2'
release = '1.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
6 changes: 3 additions & 3 deletions docs/index.rst
Expand Up @@ -18,19 +18,19 @@ How it work

Main body
+++++++++
.. automodule:: smw_core.watcher
.. automodule:: watcher.watcher
:members:
:undoc-members:

Managing configurations
+++++++++++++++++++++++
.. automodule:: smw_core.conf
.. automodule:: watcher.conf
:members:
:undoc-members:

A module for simple functions
+++++++++++++++++++++++++++++
.. automodule:: smw_core.mod
.. automodule:: watcher.mod
:members:
:undoc-members:

Expand Down
File renamed without changes.
147 changes: 147 additions & 0 deletions interface/dialog.py
@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
#!/usr/bin/python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

from os import path

def del_dir_dialog(parent, directory):
dialog = Gtk.MessageDialog(parent, 0, Gtk.MessageType.QUESTION,
Gtk.ButtonsType.YES_NO, "Ne plus surveiller " + directory + " ?")
response = dialog.run()
return response == Gtk.ResponseType.YES, dialog

class Settings(Gtk.Dialog):
"""docstring for Settings"""
def __init__(self, parent):
Gtk.Dialog.__init__(self, 'Préférences', parent, 0)
self.parent = parent
self.set_modal(True)
self.set_resizable(False)
self.set_border_width(10)
self.connect('delete-event', self.close)
box = self.get_content_area()
box.set_spacing(6)

box_time = self.create_box_time()
box_ext = self.create_box_ext()
box_files = self.create_box_files()
box_dirs = self.create_box_dirs()
box_archive_dir = self.create_box_archive_dir()
button_close = Gtk.Button.new_with_label('Femrer')
button_close.connect('clicked', self.close)

# Pack boxes
box.pack_start(box_time, False, False, 0)
box.pack_start(box_ext, False, False, 0)
box.pack_start(box_files, False, False, 0)
box.pack_start(box_dirs, False, False, 0)
box.pack_start(box_archive_dir, False, False, 0)
box.pack_start(button_close, False, False, 0)
self.show_all()

def create_box_time(self):
# Set timedelta:
box_time = Gtk.Box(spacing=6)
adjustment = Gtk.Adjustment(10, 1, 60, 10, 10, 0)
self.spinbutton = Gtk.SpinButton(adjustment=adjustment)
self.spinbutton.set_digits(0)
self.spinbutton.set_value(self.parent.config['time_delta'])
timedelta_button = Gtk.Button.new_with_label('Changer')
timedelta_button.connect('clicked', self.change_timedelta)
box_time.pack_start(Gtk.Label('Scan tous les :'), False, False, 0)
box_time.pack_start(self.spinbutton, False, False, 0)
box_time.pack_start(Gtk.Label('Minutes. '), False, False, 0)
box_time.pack_start(timedelta_button, False, False, 0)
return box_time

def create_box_ext(self):
# Exclude extenstions:
box_ext = Gtk.Box(spacing=6)
box_ext.pack_start(Gtk.Label('Extensions : '), False, False, 0)
ext_list = Gtk.ComboBoxText.new_with_entry()
button_add_ext = Gtk.Button.new_with_label('Ajouter')
button_add_ext.connect('clicked', lambda arg: self.add('exclude_ext', ext_list))
button_del_ext = Gtk.Button.new_with_label('Supprimer')
button_del_ext.connect('clicked', lambda arg: self.delete('exclude_ext', ext_list))
box_ext.pack_start(ext_list, False, False, 0)
box_ext.pack_start(button_add_ext, False, False, 0)
box_ext.pack_start(button_del_ext, False, False, 0)
for ext in self.parent.config['exclude_ext']:
ext_list.append_text(ext)
return box_ext

def create_box_files(self):
# Exclude files:
box_files = Gtk.Box(spacing=6)
box_files.pack_start(Gtk.Label('Fichiers : '), False, False, 0)
file_list = Gtk.ComboBoxText.new_with_entry()
button_add_file = Gtk.Button.new_with_label('Ajouter')
button_add_file.connect('clicked', lambda arg: self.add('exclude_files', file_list))
button_del_file = Gtk.Button.new_with_label('Supprimer')
button_del_file.connect('clicked', lambda arg: self.delete('exclude_files', file_list))
box_files.pack_start(file_list, False, False, 0)
box_files.pack_start(button_add_file, False, False, 0)
box_files.pack_start(button_del_file, False, False, 0)
for file in self.parent.config['exclude_files']:
file_list.append_text(file)
return box_files

def create_box_dirs(self):
# Exclude directories:
box_dirs = Gtk.Box(spacing=6)
box_dirs.pack_start(Gtk.Label('Dossiers : '), False, False, 0)
dirs_list = Gtk.ComboBoxText.new_with_entry()
button_add_dirs = Gtk.Button.new_with_label('Ajouter')
button_add_dirs.connect('clicked', lambda arg: self.add('exclude_dirs', dirs_list))
button_del_dirs = Gtk.Button.new_with_label('Supprimer')
button_del_dirs.connect('clicked', lambda arg: self.delete('exclude_dirs', dirs_list))
box_dirs.pack_start(dirs_list, False, False, 0)
box_dirs.pack_start(button_add_dirs, False, False, 0)
box_dirs.pack_start(button_del_dirs, False, False, 0)
for dirs in self.parent.config['exclude_dirs']:
dirs_list.append_text(dirs)
return box_dirs

def create_box_archive_dir(self):
# Archive directory:
box_archive_dir = Gtk.Box(spacing=6)
box_archive_dir.pack_start(Gtk.Label('Dossier archive : '), False, False, 0)
self.archive_entry = Gtk.Entry()
self.archive_entry.set_text(self.parent.config['archive_dir'])
archive_button = Gtk.Button.new_with_label('Changer')
archive_button.connect('clicked', self.change_archive_dir)
box_archive_dir.pack_start(self.archive_entry, False, False, 0)
box_archive_dir.pack_start(archive_button, False, False, 0)
return box_archive_dir


def close(self, *args):
self.destroy()

def add(self, elt, combo_list):
tree_iter = combo_list.get_active_iter()
if tree_iter is None:
new = combo_list.get_child().get_text()
if new not in self.parent.config[elt]:
combo_list.append_text(new)
self.parent.config[elt].append(new)

def delete(self, elt, combo_list):
tree_iter = combo_list.get_active_iter()
if tree_iter is not None:
model = combo_list.get_model()
new = model[tree_iter][0]
combo_list.remove(int(combo_list.get_active()))
self.parent.config[elt].remove(new)

def change_timedelta(self, button):
timedelta = int(self.spinbutton.get_value())
self.parent.config['time_delta'] = timedelta

def change_archive_dir(self, button):
new = self.archive_entry.get_text()
if path.exists(path.dirname(new)) or path.dirname(new) == '':
self.parent.config['archive_dir'] = new
115 changes: 115 additions & 0 deletions interface/interface.py
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
#!/usr/bin/python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import threading
from os import path
from platform import system

from .menubar import create_menus
from .tools import about, MainGrid
from .dialog import del_dir_dialog, Settings
from watcher.watcher import Watcher
import watcher

SYSTEM = system()
if SYSTEM == 'Linux':
from subprocess import Popen
elif SYSTEM == 'Windows':
from os import startfile
else:
watcher.mod.tell('Import Error')

class MyWindow(Gtk.ApplicationWindow):
def __init__(self, app, config):
Gtk.Window.__init__(self, title='SafeMyWork 1.0', application=app)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_border_width(5)

self.grid = MainGrid(self)
self.add(self.grid)
self.grid.switch_start.do_grab_focus(self.grid.switch_start)

create_menus(self)

self.config = config
self.watcher = Watcher(self.config)
self.timer = None
self.thread = None

self.initialize_config()

def initialize_config(self):
for watched_dir in self.config['watched_dirs']:
self.grid.watched_list.append_text(watched_dir)

def save_config(self, config):
watcher.mod.tell('Save config')
watcher.conf.save_config(config)

def watch(self, loop):
"""Launch watch"""
self.grid.spinner.start()
self.watcher.watch()
self.grid.spinner.stop()
if loop:
self.timer = threading.Timer(self.config['time_delta']*60, self.start_watching, args=(loop,))
self.timer.start()

def start_watching(self, loop):
"""Start watching : watch + timer"""
can = True
if loop:
can = self.grid.switch_start.get_active()
for thread in threading.enumerate():
if thread.name == 'watcher_loop' or thread.name == 'watcher_alone':
if thread.is_alive():
can = False
if can:
self.thread = threading.Thread(target=self.watch, name='watcher_loop', args=(loop,))
self.thread.start()
if loop:
self.grid.text.set_text('Surveillance active')

def stop_watching(self):
"""Cancel timer"""
if self.timer is not None:
self.timer.cancel()
self.grid.text.set_text('En attente...')

def abort_watch(self):
"""Abort current watch"""
self.watcher.stop = True

def show_saved(self, *args):
if SYSTEM == 'Linux':
Popen(['xdg-open', self.config['archive_dir']])
elif SYSTEM == 'Windows':
startfile(self.config['archive_dir'])

def settings(self, action, parameter):
dialog_settings = Settings(self)
dialog_settings.run()

def add_watched_dir(self, button):
tree_iter = self.grid.watched_list.get_active_iter()
if tree_iter is None:
new_dir = self.grid.watched_list.get_child().get_text()
if new_dir != '' and new_dir not in self.config['watched_dirs'] and path.exists(new_dir):
self.grid.watched_list.append_text(new_dir)
self.config['watched_dirs'].append(new_dir)
self.grid.text.set_text('Dossier ajouté')

def del_watched_dir(self, button):
tree_iter = self.grid.watched_list.get_active_iter()
if tree_iter is not None:
model = self.grid.watched_list.get_model()
directory = model[tree_iter][0]
must_del, dialog = del_dir_dialog(self, directory)
dialog.destroy()
if must_del:
self.config['watched_dirs'].remove(directory)
self.grid.watched_list.remove(int(self.grid.watched_list.get_active()))
self.grid.text.set_text('Dossier supprimé')
47 changes: 47 additions & 0 deletions interface/menubar.py
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
#!/usr/bin/python3

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gio

from .tools import about

def create_menus(parent):
show_saved_action = Gio.SimpleAction.new('show_saved')
show_saved_action.connect('activate', parent.show_saved)
parent.add_action(show_saved_action)

settings_action = Gio.SimpleAction.new('settings')
settings_action.connect('activate', parent.settings)
parent.add_action(settings_action)

start_watching_action = Gio.SimpleAction.new('start_watching')
start_watching_action.connect('activate', start_watching, parent)
parent.add_action(start_watching_action)

stop_watching_action = Gio.SimpleAction.new('stop_watching')
stop_watching_action.connect('activate', stop_watching, parent)
parent.add_action(stop_watching_action)

watch_now_action = Gio.SimpleAction.new('watch_now')
watch_now_action.connect('activate', wtach_now, parent)
parent.add_action(watch_now_action)

about_action = Gio.SimpleAction.new('about')
about_action.connect('activate', show_about, parent)
parent.add_action(about_action)

def start_watching(action, parameter, parent):
if not parent.grid.switch_start.get_active():
parent.grid.switch_start.set_active(True)

def stop_watching(action, parameter, parent):
if parent.grid.switch_start.get_active():
parent.grid.switch_start.set_active(False)

def show_about(action, parameter, parent):
about(parent)

def wtach_now(action, parameter, parent):
parent.start_watching(False)

0 comments on commit cc45a9c

Please sign in to comment.