diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | TODO.md | 9 | ||||
-rw-r--r-- | __init__.py | 0 | ||||
-rw-r--r-- | config.cfg.dist | 5 | ||||
-rw-r--r-- | maconomy/__init__.py | 4 | ||||
-rw-r--r-- | maconomy/cli.py | 13 | ||||
-rw-r--r-- | maconomy/mailer.py | 20 | ||||
-rw-r--r-- | maconomy/models.py | 25 | ||||
-rw-r--r-- | maconomy/repositories.py | 35 | ||||
-rw-r--r-- | maconomy/templates.py | 40 | ||||
-rw-r--r-- | maconomy_hours.py | 21 | ||||
-rw-r--r-- | requirements.txt | 1 | ||||
-rw-r--r-- | templates/ceo.html | 3 | ||||
-rw-r--r-- | templates/manager.html | 10 | ||||
-rw-r--r-- | templates/missing.html | 6 | ||||
-rw-r--r-- | templates/unsubmitted.html | 5 | ||||
-rw-r--r-- | test/__init__.py | 0 | ||||
-rw-r--r-- | test/test_templates.py | 61 |
18 files changed, 261 insertions, 0 deletions
@@ -79,3 +79,6 @@ celerybeat-schedule # Spyder project settings .spyderproject + +*.cfg +*.conf @@ -0,0 +1,9 @@ +# TODO + +- Simple CLI version +- Properties for db + passwords +- SQLalchemy for db management +- Flask frontend? +- Maconomy Rest version + - If not then SOAP? + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/__init__.py diff --git a/config.cfg.dist b/config.cfg.dist new file mode 100644 index 0000000..0844421 --- /dev/null +++ b/config.cfg.dist @@ -0,0 +1,5 @@ +[db] +host= + +[mail] +server= diff --git a/maconomy/__init__.py b/maconomy/__init__.py new file mode 100644 index 0000000..09f2516 --- /dev/null +++ b/maconomy/__init__.py @@ -0,0 +1,4 @@ +from maconomy.models import Employee, Timesheet +from maconomy.repositories import create_db, TimeRegistrationRepository +from maconomy.mailer import Mailer +from maconomy.templates import UnsubmittedEmailTemplate, MissingEmailTemplate, ManagerEmailTemplate diff --git a/maconomy/cli.py b/maconomy/cli.py new file mode 100644 index 0000000..0a11db5 --- /dev/null +++ b/maconomy/cli.py @@ -0,0 +1,13 @@ +import argparse +from ConfigParser import SafeConfigParser + +def parse(): + parser = argparse.ArgumentParser(description="Notifies people of missing hours registration") + parser.add_argument("-c", "--config", required=True) + parser.add_argument("--dry", action="store_true", default=False, help="Do not send emails to users, but print affected users instead") + return parser.parse_args() + +def load_config(conf_file): + config = SafeConfigParser() + config.read(conf_file) + return config diff --git a/maconomy/mailer.py b/maconomy/mailer.py new file mode 100644 index 0000000..ab443c0 --- /dev/null +++ b/maconomy/mailer.py @@ -0,0 +1,20 @@ +import smtplib +from email.mime.text import MIMEText + +class Mailer: + def __init__(self, config): + self.me = config.get("mail", "from") + server_addr = config.get("mail", "server") + self.server = smtplib.SMTP(server_addr) + + def send(to, subject, body): + msg = MIMEText(body,'plain') + msg['To']=to + msg['From']=self.me + msg['Subject'] = subject + self.server.sendmail(self.me, to, msg.as_string()) + + def close(self): + self.server.quit() + + diff --git a/maconomy/models.py b/maconomy/models.py new file mode 100644 index 0000000..15fb46b --- /dev/null +++ b/maconomy/models.py @@ -0,0 +1,25 @@ + +class Employee: + def __init__(self, result): + self.id, self.name, self.email = result[:3] + + def __unicode__(self): + return u"{} ({})".format(self.name, self.id) + + +class Timesheet: + def __init__(self, result): + self.week, self.submitted, self.approved = result[:3] + + def status(self): + return "submitted" if self.is_submitted() else "unsubmitted" + def is_submitted(self): + return self.submitted == 1 + def is_missing(self): + return self.submitted is None + def is_approved(self): + return self.approved == 1 + def __str__(self): + return self.__unicode__() + def __unicode__(self): + u"{} ({})".format(self.week, self.status) diff --git a/maconomy/repositories.py b/maconomy/repositories.py new file mode 100644 index 0000000..fb143e8 --- /dev/null +++ b/maconomy/repositories.py @@ -0,0 +1,35 @@ +import cx_Oracle +from maconomy.models import Employee, Timesheet + +def create_db(config): + user = config.get("db", "user") + pw = config.get("db", "password") + server = config.get("db", "server") + return cx_Oracle.connect(user, pw, server) + +class DBRepository: + def __init__(self, dbcon): + self.db = dbcon + self.cursor = dbcon.cursor() + + def close(self): + self.cursor.close() + +class TimeRegistrationRepository(DBRepository): + def all_active(self): + query = """SELECT e.EMPLOYEENUMBER,e.NAME1,e.ELECTRONICMAILADDRESS, t.WEEKNUMBER, t.SUBMITTED, t.APPROVED from EMPLOYEE e + LEFT OUTER JOIN TIMESHEETHEADER t + ON e.EMPLOYEENUMBER = t.EMPLOYEENUMBER + and t.PERIODSTART=to_char(trunc(sysdate-7, 'IW'),'YYYY.MM.DD') + WHERE e.blocked=0 + and (e.DATEENDEMPLOYMENT >= to_char(sysdate,'YYYY.MM.DD') or e.DATEENDEMPLOYMENT = ' ') + and e.employeenumber <> '99' + and e.MUSTUSETIMESHEETS=1 + ORDER BY e.employeenumber + """ + + res = self.cursor.execute(query) + rows = res.fetchall() + + return [(Employee(r), Timesheet(r[3:])) for r in rows] + diff --git a/maconomy/templates.py b/maconomy/templates.py new file mode 100644 index 0000000..07cbeb1 --- /dev/null +++ b/maconomy/templates.py @@ -0,0 +1,40 @@ +from string import Template + +class BaseTemplate(object): + def __init__(self, template_file): + self.set_template(template_file) + + def build(self, **kwargs): + return self.template.substitute(**kwargs) + + def set_template(self, template_file): + with open(template_file, 'r') as f: + template_base = f.read() + self.template = Template(template_base) + + +class UnsubmittedEmailTemplate(BaseTemplate): + def __init__(self): + self.set_template("templates/unsubmitted.html") + + def build(self, week, maconomyurl, helpurl): + return self.template.substitute(week=week, maconomyurl=maconomyurl, helpurl=helpurl) + +class MissingEmailTemplate(BaseTemplate): + def __init__(self): + self.set_template("templates/missing.html") + + def build(self, maconomyurl, helpurl): + return self.template.substitute(maconomyurl=maconomyurl, helpurl=helpurl) + +class ManagerEmailTemplate(BaseTemplate): + def __init__(self): + self.set_template("templates/manager.html") + + def build(self, employee, week, maconomyurl, **kwargs): + submitted = '' if 'submitted' in kwargs and kwargs['submitted'] else 'not' + approved = '' if 'approved' in kwargs and kwargs['approved'] else 'not' + return self.template.substitute(employee=employee.__unicode__(),week=week,maconomyurl=maconomyurl, submitted=submitted, approved=approved) + + + diff --git a/maconomy_hours.py b/maconomy_hours.py new file mode 100644 index 0000000..1001964 --- /dev/null +++ b/maconomy_hours.py @@ -0,0 +1,21 @@ +from maconomy import cli, Employee, Timesheet, create_db, TimeRegistrationRepository + +def main(config, dry_run): + db = create_db(config) + timereg_repo = TimeRegistrationRepository(db) + + for employee, timesheet in timereg_repo.all_active(): + if timesheet.is_missing(): + print u"Timesheet for {} has not been created".format(employee) + elif not timesheet.is_submitted(): + print u"Missing time sheet for {}".format(employee) + elif not timesheet.is_approved(): + print u"Timesheet for {} not approved".format(employee) + + timereg_repo.close() + db.close() + +if __name__ == '__main__': + args = cli.parse() + config = cli.load_config(args.config) + main(config, args.dry) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a491756 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +cx_Oracle diff --git a/templates/ceo.html b/templates/ceo.html new file mode 100644 index 0000000..ebc2ea9 --- /dev/null +++ b/templates/ceo.html @@ -0,0 +1,3 @@ +<p>Time registration status mail.</p> + +$status diff --git a/templates/manager.html b/templates/manager.html new file mode 100644 index 0000000..6e956e2 --- /dev/null +++ b/templates/manager.html @@ -0,0 +1,10 @@ +<p>Missing time registration for your employee $employee, please submit and approve before tuesday 2 AM (CET)</p> + +<p>Status for $employee in Week $week + <ul> + <li>Timesheet has $submitted been submitted</li> + <li>Timesheet has $approved been approved by you.</li> + </ul> +</p> + +<p>Please go to <a href="$maconomyurl">Maconomy Portal</a> to submit and approve time.</p> diff --git a/templates/missing.html b/templates/missing.html new file mode 100644 index 0000000..c17c8fc --- /dev/null +++ b/templates/missing.html @@ -0,0 +1,6 @@ +<p>Timesheet for last week has not been created.</p> + +<p>Please go to <a href="$maconomyurl">Maconomy Portal</a> to register time.</p> + +<p>For information on how to use time registration please see <a href="$helpurl">Time registration in Maconomy</a></p> + diff --git a/templates/unsubmitted.html b/templates/unsubmitted.html new file mode 100644 index 0000000..73af949 --- /dev/null +++ b/templates/unsubmitted.html @@ -0,0 +1,5 @@ +<p>Timesheet for week $week has not been submitted.</p> + +<p>Please go to <a href="$maconomyurl">Maconomy Portal</a> to register time.</p> + +<p>For information on how to use time registration please see <a href="$helpurl">Time registration in Maconomy</a></p> diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/__init__.py diff --git a/test/test_templates.py b/test/test_templates.py new file mode 100644 index 0000000..ee05ac6 --- /dev/null +++ b/test/test_templates.py @@ -0,0 +1,61 @@ +from maconomy import UnsubmittedEmailTemplate, MissingEmailTemplate, ManagerEmailTemplate, Employee +import unittest + +class UnsubmittedEmailTemplateTest(unittest.TestCase): + + def setUp(self): + self.template = UnsubmittedEmailTemplate() + + def test_substitution(self): + result = self.template.build(week=10, maconomyurl="http://localhost/", helpurl="http://example.com") + self.assertIn("week 10", result) + self.assertIn("href=\"http://localhost/\"", result) + self.assertIn("href=\"http://example.com\"", result) + +class MissingEmailTemplateTest(unittest.TestCase): + + def setUp(self): + self.template = MissingEmailTemplate() + + def test_substitution(self): + result = self.template.build(maconomyurl="http://localhost/", helpurl="http://example.com") + self.assertIn("href=\"http://localhost/\"", result) + self.assertIn("href=\"http://example.com\"", result) + +class ManagerEmailTemplateTest(unittest.TestCase): + + def setUp(self): + self.template = ManagerEmailTemplate() + self.employee = Employee(("MK", "Markus Krogh", "markus@nordu.net")) + + def test_substitute(self): + result = self.template.build( + employee=self.employee, + week=11, + maconomyurl="http://localhost/", + ) + self.assertIn("Markus Krogh (MK)", result) + self.assertIn("Week 11", result) + self.assertIn("not been submitted", result) + self.assertIn("not been approved", result) + self.assertIn("href=\"http://localhost/\"", result) + + def test_submitted(self): + result = self.template.build( + employee=self.employee, + week=11, + maconomyurl="http://localhost/", + submitted = True, + ) + self.assertIn("has been submitted", result) + def test_approved(self): + result = self.template.build( + employee=self.employee, + week=11, + maconomyurl="http://localhost/", + approved = True, + ) + self.assertIn("has been approved", result) + + + |