Browse code
initial --- models done ? init view
fiddlerwoaroof authored on 30/01/2016 06:39:40
Showing 6 changed files
Showing 6 changed files
0 | 2 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,126 @@ |
1 |
+ |
|
2 |
+(in-package :timesheet.mvc) |
|
3 |
+ |
|
4 |
+(defclass model () ()) |
|
5 |
+ |
|
6 |
+(defclass view () ()) |
|
7 |
+ |
|
8 |
+(defclass controller () ()) |
|
9 |
+ |
|
10 |
+(defclass event () ()) |
|
11 |
+ |
|
12 |
+(defgeneric operate (controller model event)) |
|
13 |
+(defgeneric model-changed (model receiver)) |
|
14 |
+ |
|
15 |
+(defclass client (model) |
|
16 |
+ ((name :initarg :name) |
|
17 |
+ (projects :initarg :projects :initform nil))) |
|
18 |
+ |
|
19 |
+(defun make-client (name) |
|
20 |
+ (make-instance 'client :name name)) |
|
21 |
+ |
|
22 |
+(defun normalize-name (name) |
|
23 |
+ (string-capitalize name)) |
|
24 |
+ |
|
25 |
+(defparameter *clients* nil) |
|
26 |
+(defun make-or-retrieve-client (name) |
|
27 |
+ (let ((name (normalize-name name))) |
|
28 |
+ (cdr (or (assoc name *clients* :test #'string=) |
|
29 |
+ (car (push (cons name (make-client name)) *clients*)))))) |
|
30 |
+ |
|
31 |
+(defclass project-description (model) |
|
32 |
+ ((client :initarg :client :type 'client) |
|
33 |
+ (note :initarg :note :initform ""))) |
|
34 |
+ |
|
35 |
+(defun make-project-description (client-name note) |
|
36 |
+ (make-instance 'project-description |
|
37 |
+ :client (make-or-retrieve-client client-name) |
|
38 |
+ :note note)) |
|
39 |
+ |
|
40 |
+(defclass generic-log (model) |
|
41 |
+ ((entries :initarg :entries :initform nil))) |
|
42 |
+ |
|
43 |
+(defgeneric most-recent-entry (container) |
|
44 |
+ (:method ((container log-entry)) |
|
45 |
+ (with-slots (entries) container |
|
46 |
+ (car entries)))) |
|
47 |
+ |
|
48 |
+(defclass generic-log-entry (model) ()) |
|
49 |
+ |
|
50 |
+(defun %add-entry (container entry) |
|
51 |
+ (push entry (slot-value container 'entries))) |
|
52 |
+ |
|
53 |
+(defgeneric add-entry (log entry)) |
|
54 |
+ |
|
55 |
+(defclass time-log (generic-log) |
|
56 |
+ ((date :initarg :date :initform (local-time:today)))) |
|
57 |
+ |
|
58 |
+(defun make-time-log (&optional date) |
|
59 |
+ (make-instance 'time-log :date (or date (local-time:today)))) |
|
60 |
+ |
|
61 |
+(defclass time-span (model) |
|
62 |
+ ((start :initarg :start :initform (local-time:now)) |
|
63 |
+ (end :initarg :end))) |
|
64 |
+ |
|
65 |
+(defclass time-log-entry (generic-log) |
|
66 |
+ ((description :initarg :description :type 'project-description))) |
|
67 |
+ |
|
68 |
+(defmethod add-entry ((time-log-entry time-log-entry) (entry time-span)) |
|
69 |
+ (%add-entry time-log-entry entry)) |
|
70 |
+ |
|
71 |
+(defmethod add-entry ((time-log time-log) (entry time-log-entry)) |
|
72 |
+ (%add-entry time-log entry)) |
|
73 |
+ |
|
74 |
+(defclass work-log (generic-log) |
|
75 |
+ ((title :initarg :title :initform "") |
|
76 |
+ (user :initarg :user :initform ""))) |
|
77 |
+ |
|
78 |
+(defmethod add-entry ((work-log work-log) (entry time-log)) |
|
79 |
+ (%add-entry work-log entry)) |
|
80 |
+ |
|
81 |
+(defun make-work-log (title user) |
|
82 |
+ (make-instance 'work-log :title title :user user)) |
|
83 |
+ |
|
84 |
+ |
|
85 |
+;; VIEW |
|
86 |
+ |
|
87 |
+(defgeneric display (model view output)) |
|
88 |
+ |
|
89 |
+(defclass log-view (view) ()) |
|
90 |
+ |
|
91 |
+(defmethod display ((model work-log) (view log-view) (output stream)) |
|
92 |
+ (let ((spinneret:*html* output)) |
|
93 |
+ (with-slots (title user entries) model |
|
94 |
+ (spinneret:with-html |
|
95 |
+ (:html |
|
96 |
+ (:head |
|
97 |
+ (:title (format nil "~a: ~a" user title))) |
|
98 |
+ (:body |
|
99 |
+ (:h1 title) |
|
100 |
+ (:ul.work-log |
|
101 |
+ (loop for time-log in entries |
|
102 |
+ do (with-slots (date entries) time-log |
|
103 |
+ (spinneret:with-html |
|
104 |
+ (:li |
|
105 |
+ (:div.date date) |
|
106 |
+ (:ul.time-entries |
|
107 |
+ (:li.time-span |
|
108 |
+ (loop for time-entry in entries |
|
109 |
+ do (with-slots (description entries) time-entry |
|
110 |
+ (spinneret:with-html |
|
111 |
+ (:div.description |
|
112 |
+ (loop for time-entry in entries |
|
113 |
+ do (with-slots (start end) time-entry |
|
114 |
+ (:div.timespan |
|
115 |
+ (:span.start (format nil "~a" start)) |
|
116 |
+ (when end |
|
117 |
+ (:span.end (format nil "~a" end)))))) |
|
118 |
+ (with-slots (client note) description |
|
119 |
+ (spinneret:with-html |
|
120 |
+ (:span.name (with-slots (name) client |
|
121 |
+ (format nil "~a:" name))) |
|
122 |
+ (:span.note note)))))))))))))))))))) |
|
123 |
+ |
|
124 |
+(defmethod display (model view (output (eql nil))) |
|
125 |
+ (with-output-to-string (s) |
|
126 |
+ (display model view s))) |
0 | 9 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,19 @@ |
1 |
+;;;; timesheet.asd |
|
2 |
+ |
|
3 |
+(asdf:defsystem #:timesheet |
|
4 |
+ :description "Describe timesheet here" |
|
5 |
+ :author "fiddlerwoaroof" |
|
6 |
+ :license "MIT" |
|
7 |
+ :depends-on (#:alexandria |
|
8 |
+ #:serapeum |
|
9 |
+ #:anaphora |
|
10 |
+ #:ningle |
|
11 |
+ #:spinneret |
|
12 |
+ ) |
|
13 |
+ :serial t |
|
14 |
+ :components ((:file "package") |
|
15 |
+ (:file "mvc") |
|
16 |
+ (:file "timesheet"))) |
|
17 |
+ |
|
18 |
+ |
|
19 |
+;; vim: set ft=lisp: |