summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--TODO.md9
-rw-r--r--__init__.py0
-rw-r--r--config.cfg.dist5
-rw-r--r--maconomy/__init__.py4
-rw-r--r--maconomy/cli.py13
-rw-r--r--maconomy/mailer.py20
-rw-r--r--maconomy/models.py25
-rw-r--r--maconomy/repositories.py35
-rw-r--r--maconomy/templates.py40
-rw-r--r--maconomy_hours.py21
-rw-r--r--requirements.txt1
-rw-r--r--templates/ceo.html3
-rw-r--r--templates/manager.html10
-rw-r--r--templates/missing.html6
-rw-r--r--templates/unsubmitted.html5
-rw-r--r--test/__init__.py0
-rw-r--r--test/test_templates.py61
18 files changed, 261 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 908a064..c239cc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,3 +79,6 @@ celerybeat-schedule
# Spyder project settings
.spyderproject
+
+*.cfg
+*.conf
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..2bc4e9f
--- /dev/null
+++ b/TODO.md
@@ -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)
+
+
+