Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/actions/action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from abc import ABC, abstractmethod


class Action(ABC):
"""Abstract class for all actioner to implement"""

@abstractmethod
def process(self):
"""Process the file. Should return the file back for chaining."""
pass


class MoveAction(Action):
def __init__(self, destination):
self.destination = destination

def process(self, file):
# TODO: Implement
print("Pretending to move '{}' to '{}'".format(file, self.destination))
return file


class RenameAction(Action):
def __init__(self, regex):
self.regex = regex

def process(self, file):
# TODO: Implement
print("Pretending to rename '{}' to '{}'".format(file, self.regex))
return file
60 changes: 27 additions & 33 deletions src/filed.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,54 @@

from os import walk, path, listdir

from predicates.predicate import *
from actions.action import *


class Rule(object):
src = None
dst = None
reg = None
predicates = None
actions = None


def parse_config_file(fd):
"""Parses the file descriptor line by line. Skips comments. This also
validates all the configurations, therefore the return value should be safe
and usable.
"""

rules = []

for num, line in enumerate(fd):
line = line.strip()

# Skip comments
if line.startswith('#'):
if line.startswith('#') or not len(line) > 1:
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe that condition could be rewritten?
len(line) == 0 or len(line) <= 0

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I do just 'len(line)' is python? Like with truthy falsy values?

Copy link
Copy Markdown
Collaborator

@ejwmoreau ejwmoreau Jun 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, not len(line) works too. Not sure which is better.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also do not line because "" is considered falsey

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool!

continue

# Format of the line is src:dst:regexpression
# See resource/filedrc for example format
parts = line.split(':')
if len(parts) != 3:
logging.warning("{}:{}: Error reading line".format(filename, num))
logging.warning("{}:{}: Error reading line".format(line, num))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filename gives you the config file that is having a problem. The line itself should be part of the error, IMO.

continue

# Validate the parts of the config
r = Rule()
r.src = path.expanduser(parts[0])
r.dst = path.expanduser(parts[1])
r.reg = parts[2]

if not path.isdir(r.src):
logging.warning(
"{}:{}: {} is not a directory"
.format(filename, num, r.src))
try:
# TODO: Bad things will happen if pipe symbol inside the function
# TODO: Avoid using eval()
r.predicates = list(map(eval, parts[1].split('|')))
r.actions = list(map(eval, parts[2].split('|')))
except RuntimeError:
# TODO: Doing proper exceptions.
logging.warning("Something went wrong")
continue

if not path.isdir(r.dst):
if not path.isdir(r.src):
logging.warning(
"{}:{}: {} is not a directory"
.format(filename, num, r.dst))

try:
re.compile(r.reg)
except Exception as e:
logging.warning(
"{}:{}: error parsing regex: {}"
.format(filename, num, str(e)))
.format(line, num, r.src))

rules.append(r)

Expand Down Expand Up @@ -82,7 +80,7 @@ def load_config_file(filename):

def main():
config_files = [
'~/.filedrc'
path.join(path.dirname(__file__), 'resource/filedrc')
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we'll be getting a user's config from the "resource/filedrc" file? Good for testing but maybe we need to allow a certain file/folder for all user's configs.

Okay for now, just something that would need to be discussed.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. I should have mentioned that was just to wire up the example configure file.

We still need to figure out how we want to lay out the confit file.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, sounds good :)

]

rules = {}
Expand All @@ -92,20 +90,16 @@ def main():
# Monitor folder for changes
for filename, rules in rules.items():
for rule in rules:
matches = matcher(rule.src, rule.reg)
print(matches)
files = listdir(path.expanduser(rule.src))

# TODO: Move the file if changes
for predicate in rule.predicates:
files = filter(predicate.match, files)

for action in rule.actions:
files = map(action.process, files)

# Returns the file path that match any of the rules
def matcher(source, rule):
f = []
for filename in listdir(path.expanduser(source)):
if re.match(rule, filename):
f.append(path.join(source, filename))
# TODO: Future subtrees - for (dirpath, dirnames, filenames) in walk(path.expanduser(s)):
return f
# Because map and filters are lazy function. This is needed to execute map and filters
list(files)

if __name__ == "__main__":
main()
18 changes: 18 additions & 0 deletions src/predicates/predicate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import re

from abc import ABC, abstractmethod


class Predicate(ABC):
@abstractmethod
def match(self):
""" Returns true if file satisfies predicate. False otherwise. """
pass


class RegexPredicate(Predicate):
def __init__(self, regex_str):
self.regex = re.compile(regex_str)

def match(self, path):
return bool(self.regex.match(path))
7 changes: 7 additions & 0 deletions src/resource/filedrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Filed example config
# src:predicates:actions
# predicates = predicate1() | predicate2() | ...
# actions = action1() | action2() | ...

# Moves all *.pdf files into ~/documents
~/downloads/:RegexPredicate(".*\.pdf"):MoveAction("~/documents/")