Compare commits
	
		
			26 commits
		
	
	
		
			9b165490e5
			...
			85af3fcff3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85af3fcff3 | |||
| 8b6e1955ef | |||
| 2a7fb0d735 | |||
| e1bb1885b2 | |||
| 0a762ccb1d | |||
| 6915cc0e21 | |||
| 6947dd37b3 | |||
| d24b7c4136 | |||
| fabb387ba1 | |||
| 708268d91d | |||
| a9f5fc74e4 | |||
| c458dc3900 | |||
| dcf6d8937f | |||
| 15888b7e3e | |||
| dc3044026c | |||
| ed55660c80 | |||
| d0771e130a | |||
| 6282a934c6 | |||
| 707bb1d61e | |||
| ba2c753109 | |||
| 2674f08674 | |||
| 3629844743 | |||
| 65c7155ba3 | |||
| 939af54e87 | |||
| a64ab232c6 | |||
| 2baffe570b | 
					 14 changed files with 237 additions and 104 deletions
				
			
		
							
								
								
									
										35
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										35
									
								
								CHANGELOG.md
									
										
									
									
									
								
							|  | @ -1,14 +1,41 @@ | ||||||
| ChangeLog | ChangeLog | ||||||
| ========= | ========= | ||||||
| 
 | 
 | ||||||
| 1.15 | 1.16.2 - released 2024-05-07 | ||||||
| ---- | ---------------------------- | ||||||
|  | 
 | ||||||
|  | * fix rada-ml-pred? in -mlsync | ||||||
|  | 
 | ||||||
|  | 1.16.1 - released 2024-04-02 | ||||||
|  | ---------------------------- | ||||||
|  | 
 | ||||||
|  | * add EUR account for paying membership fees to member's page | ||||||
|  | 
 | ||||||
|  | 1.16 - released 2024-02-09 | ||||||
|  | -------------------------- | ||||||
|  | 
 | ||||||
|  | * handle # character at weird positions in DokuWiki users.auth.php | ||||||
|  | * unify -mlsync and -mlcheck handling of member predicates | ||||||
|  | * handle unicode characters with 3-byte UTF-8 representation correctly | ||||||
|  | * calculate expected income with respect to discounts granted | ||||||
|  | * report soon-expiring members in the summary emails | ||||||
|  | * report mailing lists check status in summary emails | ||||||
|  | 
 | ||||||
|  | 1.15.1 - released 2024-01-02 | ||||||
|  | ---------------------------- | ||||||
|  | 
 | ||||||
|  | * fix calculating historical membership fee (was erroneously based on | ||||||
|  |   current date) | ||||||
|  | * fix showing basic information without MLs loaded | ||||||
|  | 
 | ||||||
|  | 1.15 - released 2024-12-24 | ||||||
|  | -------------------------- | ||||||
| 
 | 
 | ||||||
| * increase membership fees starting 2024-01 (specification.rkt) | * increase membership fees starting 2024-01 (specification.rkt) | ||||||
| * add support for explicit fee amounts for specified period | * add support for explicit fee amounts for specified period | ||||||
| 
 | 
 | ||||||
| 1.14 | 1.14 - released 2024-12-06 | ||||||
| ---- | -------------------------- | ||||||
| 
 | 
 | ||||||
| * add support for dynamic terminal size | * add support for dynamic terminal size | ||||||
| * use table cell formatting instead of paragraph formatting everywhere | * use table cell formatting instead of paragraph formatting everywhere | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								src/Makefile
									
										
									
									
									
								
							
							
						
						
									
										17
									
								
								src/Makefile
									
										
									
									
									
								
							|  | @ -42,7 +42,7 @@ HACKERBASE-DEPS=hackerbase.scm cal-month.import.scm			\ | ||||||
| 	tests.import.scm notifications.import.scm logging.import.scm	\
 | 	tests.import.scm notifications.import.scm logging.import.scm	\
 | ||||||
| 	progress.import.scm cal-period.import.scm			\
 | 	progress.import.scm cal-period.import.scm			\
 | ||||||
| 	util-stdout.import.scm export-web-static.import.scm		\
 | 	util-stdout.import.scm export-web-static.import.scm		\
 | ||||||
| 	dokuwiki.import.scm | 	dokuwiki.import.scm mailinglist.import.scm | ||||||
| 
 | 
 | ||||||
| HACKERBASE-OBJS=hackerbase.o testing.o listing.o cal-month.o		\
 | HACKERBASE-OBJS=hackerbase.o testing.o listing.o cal-month.o		\
 | ||||||
| 	 cal-period.o ansi.o util-bst-ldict.o command-line.o mbase.o	\
 | 	 cal-period.o ansi.o util-bst-ldict.o command-line.o mbase.o	\
 | ||||||
|  | @ -59,7 +59,8 @@ HACKERBASE-OBJS=hackerbase.o testing.o listing.o cal-month.o		\ | ||||||
| 	 template-list-expander.o box-drawing.o export-web-static.o	\
 | 	 template-list-expander.o box-drawing.o export-web-static.o	\
 | ||||||
| 	 util-dir.o dokuwiki.o racket-kwargs.o duck.o util-bst.o	\
 | 	 util-dir.o dokuwiki.o racket-kwargs.o duck.o util-bst.o	\
 | ||||||
| 	 util-bst-bdict.o util-bst-ldict.o util-bst-lset.o mailman2.o	\
 | 	 util-bst-bdict.o util-bst-ldict.o util-bst-lset.o mailman2.o	\
 | ||||||
| 	 mailman-common.o mailman3.o mailman3-sql.o tiocgwinsz.o | 	 mailman-common.o mailman3.o mailman3-sql.o tiocgwinsz.o	\
 | ||||||
|  | 	 mailinglist.o | ||||||
| 
 | 
 | ||||||
| GENDOC-SOURCES=gendoc.scm duck-extract.import.scm			\
 | GENDOC-SOURCES=gendoc.scm duck-extract.import.scm			\
 | ||||||
| 	util-time.import.scm util-csv.import.scm util-git.import.scm	\
 | 	util-time.import.scm util-csv.import.scm util-git.import.scm	\
 | ||||||
|  | @ -291,7 +292,7 @@ util-io.o: util-io.import.scm | ||||||
| util-io.import.scm: $(UTIL-IO-SOURCES) | util-io.import.scm: $(UTIL-IO-SOURCES) | ||||||
| 
 | 
 | ||||||
| UTIL-PARSER-SOURCES=util-parser.scm testing.import.scm	\
 | UTIL-PARSER-SOURCES=util-parser.scm testing.import.scm	\
 | ||||||
| 	duck.import.scm | 	duck.import.scm racket-kwargs.import.scm | ||||||
| 
 | 
 | ||||||
| util-parser.o: util-parser.import.scm | util-parser.o: util-parser.import.scm | ||||||
| util-parser.import.scm: $(UTIL-PARSER-SOURCES) | util-parser.import.scm: $(UTIL-PARSER-SOURCES) | ||||||
|  | @ -332,7 +333,8 @@ NOTIFICATIONS-SOURCES=notifications.scm brmember.import.scm		\ | ||||||
| 	brmember-format.import.scm configuration.import.scm		\
 | 	brmember-format.import.scm configuration.import.scm		\
 | ||||||
| 	util-time.import.scm members-fees.import.scm mbase.import.scm	\
 | 	util-time.import.scm members-fees.import.scm mbase.import.scm	\
 | ||||||
| 	members-print.import.scm table.import.scm			\
 | 	members-print.import.scm table.import.scm			\
 | ||||||
| 	bank-account.import.scm logging.import.scm | 	bank-account.import.scm logging.import.scm			\
 | ||||||
|  | 	mailinglist.import.scm | ||||||
| 
 | 
 | ||||||
| notifications.o: notifications.import.scm | notifications.o: notifications.import.scm | ||||||
| notifications.import.scm: $(NOTIFICATIONS-SOURCES) | notifications.import.scm: $(NOTIFICATIONS-SOURCES) | ||||||
|  | @ -550,3 +552,10 @@ TIOCGWINSZ-SOURCES=tiocgwinsz.scm duck.import.scm | ||||||
| 
 | 
 | ||||||
| tiocgwinsz.o: tiocgwinsz.import.scm | tiocgwinsz.o: tiocgwinsz.import.scm | ||||||
| tiocgwinsz.import.scm: $(TIOCGWINSZ-SOURCES) | tiocgwinsz.import.scm: $(TIOCGWINSZ-SOURCES) | ||||||
|  | 
 | ||||||
|  | MAILINGLIST-SOURCES=mailinglist.scm racket-kwargs.import.scm		\
 | ||||||
|  | 	mailman.import.scm mbase.import.scm util-string.import.scm	\
 | ||||||
|  | 	brmember.import.scm | ||||||
|  | 
 | ||||||
|  | mailinglist.o: mailinglist.import.scm | ||||||
|  | mailinglist.import.scm: $(MAILINGLIST-SOURCES) | ||||||
|  |  | ||||||
|  | @ -499,8 +499,8 @@ | ||||||
| 	       (ldict-equal? | 	       (ldict-equal? | ||||||
| 		(make-brmember '|1234| "members/1234" '(|member|)) | 		(make-brmember '|1234| "members/1234" '(|member|)) | ||||||
| 		(make-ldict | 		(make-ldict | ||||||
| 		 `((file-name . |1234|) | 		 `((TAG . ,TAG-BRMEMBER) | ||||||
| 		   (TAG . ,TAG-BRMEMBER) | 		   (file-name . |1234|) | ||||||
| 		   (file-path . "members/1234") | 		   (file-path . "members/1234") | ||||||
| 		   (symlinks |member|) | 		   (symlinks |member|) | ||||||
| 		   (id . 1234))))) | 		   (id . 1234))))) | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ | ||||||
| 	      (users '())) | 	      (users '())) | ||||||
|      (if (null? lines) |      (if (null? lines) | ||||||
| 	 users | 	 users | ||||||
| 	 (let ((line (parser-preprocess-line (car lines)))) | 	 (let ((line (parser-preprocess-line (car lines) #:strip-comments? #f))) | ||||||
| 	   (if (equal? line "") | 	   (if (equal? line "") | ||||||
| 	       (loop (cdr lines) | 	       (loop (cdr lines) | ||||||
| 		     users) | 		     users) | ||||||
|  | @ -74,8 +74,8 @@ | ||||||
|    (handle-exceptions |    (handle-exceptions | ||||||
|     exn |     exn | ||||||
|     (let () |     (let () | ||||||
|       (log-warning "DokuWiki: cannot open ~A" fname) |       (log-warning "DokuWiki (~A) exception ~A" fname (condition->list exn)) | ||||||
|       (stdout-printf "DokuWiki: cannot open ~A" fname) |       (stdout-printf "DokuWiki (~A) exception ~A" fname (condition->list exn)) | ||||||
|       '()) |       '()) | ||||||
|     (with-input-from-file fname |     (with-input-from-file fname | ||||||
|       parse-dokuwiki-users-auth))) |       parse-dokuwiki-users-auth))) | ||||||
|  |  | ||||||
|  | @ -101,7 +101,7 @@ | ||||||
|      (print "<dt>Member ID, Variable Symbol for Payments<br>(Členské číslo, variabilní symbol plateb)</dt><dd>" |      (print "<dt>Member ID, Variable Symbol for Payments<br>(Členské číslo, variabilní symbol plateb)</dt><dd>" | ||||||
| 	    (brmember-id mr) "</dd>") | 	    (brmember-id mr) "</dd>") | ||||||
|      (print "<dt>Balance in CZK<br>(Zůstatek v Kč)</dt><dd>" (caar (reverse bhs)) "</dd>") |      (print "<dt>Balance in CZK<br>(Zůstatek v Kč)</dt><dd>" (caar (reverse bhs)) "</dd>") | ||||||
|      (print "<dt>Account for Payments<br>(Účet pro platbu příspěvků)</dt><dd>2500079551/2010</dd>") |      (print "<dt>Account for Payments<br>(Účet pro platbu příspěvků)</dt><dd>CZK: 2500079551/2010<br>EUR:  CZ93 2010 0000 0021 0007 9552</dd>") | ||||||
|      (print "</dl>") |      (print "</dl>") | ||||||
|      (print "</div>") |      (print "</div>") | ||||||
|      (print "<div class=\"bi\">") |      (print "<div class=\"bi\">") | ||||||
|  |  | ||||||
|  | @ -50,7 +50,8 @@ | ||||||
| 	export-web-static | 	export-web-static | ||||||
| 	dokuwiki | 	dokuwiki | ||||||
| 	racket-kwargs | 	racket-kwargs | ||||||
| 	util-string) | 	util-string | ||||||
|  | 	mailinglist) | ||||||
| 
 | 
 | ||||||
| ;; Command-line options and configurable parameters | ;; Command-line options and configurable parameters | ||||||
| (define -needs-bank- (make-parameter #f)) | (define -needs-bank- (make-parameter #f)) | ||||||
|  | @ -286,32 +287,6 @@ | ||||||
| 	      (print "  " (car keys) ": " (length (ldict-ref status (car keys))))) | 	      (print "  " (car keys) ": " (length (ldict-ref status (car keys))))) | ||||||
| 	    (loop (cdr keys))))))) | 	    (loop (cdr keys))))))) | ||||||
| 
 | 
 | ||||||
| (define* (check-mailing-list mls name #:pred? (pred? #f)) |  | ||||||
|   (define ml (find-mailman-list mls name)) |  | ||||||
|   (when ml |  | ||||||
|     (let-values (((missing surplus) |  | ||||||
| 		  (mailman-compare-members ml |  | ||||||
| 					   (mbase-active-emails MB |  | ||||||
| 								#:suspended #t |  | ||||||
| 								#:pred? pred? |  | ||||||
| 								)))) |  | ||||||
|       (if (null? (cdr ml)) |  | ||||||
| 	  (print "Skipping ML check - not loaded") |  | ||||||
| 	  (if (and (null? missing) |  | ||||||
| 		   (null? surplus)) |  | ||||||
| 	      (print (format "~a mailing list membership in sync." (string-capitalize name))) |  | ||||||
| 	      (let () |  | ||||||
| 		(print (format "~a mailing list:" (string-capitalize name))) |  | ||||||
| 		(when (not (null? missing)) |  | ||||||
| 		  (print "  Missing: " missing)) |  | ||||||
| 		(when (not (null? surplus)) |  | ||||||
| 		  (print "  Outsiders: " surplus)))))))) |  | ||||||
| 
 |  | ||||||
| (define (rada-ml-pred? mr) |  | ||||||
|   (or (brmember-council? mr) |  | ||||||
|       (brmember-chair? mr) |  | ||||||
|       (brmember-revision? mr))) |  | ||||||
| 
 |  | ||||||
| ;; Perform requested action | ;; Perform requested action | ||||||
| (case (-action-) | (case (-action-) | ||||||
|   ((print-info) |   ((print-info) | ||||||
|  | @ -323,10 +298,7 @@ | ||||||
|        (let () |        (let () | ||||||
| 	 (print-members-base-table MB) | 	 (print-members-base-table MB) | ||||||
| 	 (newline) | 	 (newline) | ||||||
| 	 (check-mailing-list MLS "internal") | 	 (print-mailing-list-checks MB MLS) | ||||||
| 	 (check-mailing-list MLS "rada" |  | ||||||
| 			     #:pred? rada-ml-pred?) |  | ||||||
| 	 (check-mailing-list MLS "rk" #:pred? brmember-revision?) |  | ||||||
| 	 (print-git-status))) | 	 (print-git-status))) | ||||||
|    (newline)) |    (newline)) | ||||||
|   ((print-stats) |   ((print-stats) | ||||||
|  | @ -424,8 +396,8 @@ | ||||||
|    (print-git-status)) |    (print-git-status)) | ||||||
|   ((summary) |   ((summary) | ||||||
|    (if (-send-emails-) |    (if (-send-emails-) | ||||||
|        (make+send-summary-email MB) |        (make+send-summary-email MB MLS) | ||||||
|        (make+print-summary-email MB))) |        (make+print-summary-email MB MLS))) | ||||||
|   ((list) |   ((list) | ||||||
|    (for-each (lambda (mr) |    (for-each (lambda (mr) | ||||||
| 	       (print (brmember-nick mr))) | 	       (print (brmember-nick mr))) | ||||||
|  |  | ||||||
							
								
								
									
										78
									
								
								src/mailinglist.scm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/mailinglist.scm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | ;; | ||||||
|  | ;; mailinglist.scm | ||||||
|  | ;; | ||||||
|  | ;; Common high-level mailinglist management procedures. | ||||||
|  | ;; | ||||||
|  | ;; ISC License | ||||||
|  | ;; | ||||||
|  | ;; Copyright 2023 Brmlab, z.s. | ||||||
|  | ;; Dominik Pantůček <dominik.pantucek@trustica.cz> | ||||||
|  | ;; | ||||||
|  | ;; Permission to use, copy, modify, and/or distribute this software | ||||||
|  | ;; for any purpose with or without fee is hereby granted, provided | ||||||
|  | ;; that the above copyright notice and this permission notice appear | ||||||
|  | ;; in all copies. | ||||||
|  | ;;  | ||||||
|  | ;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | ||||||
|  | ;; WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | ||||||
|  | ;; WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE | ||||||
|  | ;; AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR | ||||||
|  | ;; CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS | ||||||
|  | ;; OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | ||||||
|  | ;; NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||||
|  | ;; CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||||
|  | ;; | ||||||
|  | 
 | ||||||
|  | (declare (unit mailinglist)) | ||||||
|  | 
 | ||||||
|  | (module | ||||||
|  |  mailinglist | ||||||
|  |  ( | ||||||
|  |   check-mailing-list | ||||||
|  |   print-mailing-list-checks | ||||||
|  | 
 | ||||||
|  |   rada-ml-pred? | ||||||
|  |   ) | ||||||
|  | 
 | ||||||
|  |  (import scheme | ||||||
|  | 	 (chicken base) | ||||||
|  | 	 (chicken format) | ||||||
|  | 	 racket-kwargs | ||||||
|  | 	 mailman | ||||||
|  | 	 mbase | ||||||
|  | 	 util-string | ||||||
|  | 	 brmember) | ||||||
|  | 
 | ||||||
|  |  (define* (check-mailing-list MB mls name #:pred? (pred? #f) #:suspended (suspended #f)) | ||||||
|  |    (define ml (find-mailman-list mls name)) | ||||||
|  |    (when ml | ||||||
|  |      (let-values (((missing surplus) | ||||||
|  | 		   (mailman-compare-members ml | ||||||
|  | 					    (mbase-active-emails MB | ||||||
|  | 								 #:suspended suspended | ||||||
|  | 								 #:pred? pred? | ||||||
|  | 								 )))) | ||||||
|  |        (if (null? (cdr ml)) | ||||||
|  | 	   (print "Skipping ML check - not loaded") | ||||||
|  | 	   (if (and (null? missing) | ||||||
|  | 		    (null? surplus)) | ||||||
|  | 	       (print (format "~a mailing list membership in sync." (string-capitalize name))) | ||||||
|  | 	       (let () | ||||||
|  | 		 (print (format "~a mailing list:" (string-capitalize name))) | ||||||
|  | 		 (when (not (null? missing)) | ||||||
|  | 		   (print "  Missing: " missing)) | ||||||
|  | 		 (when (not (null? surplus)) | ||||||
|  | 		   (print "  Outsiders: " surplus)))))))) | ||||||
|  | 
 | ||||||
|  |  (define (print-mailing-list-checks MB MLS) | ||||||
|  |    (check-mailing-list MB MLS "internal" #:suspended #t) | ||||||
|  |    (check-mailing-list MB MLS "rada" | ||||||
|  | 		       #:pred? rada-ml-pred?) | ||||||
|  |    (check-mailing-list MB MLS "rk" #:pred? brmember-revision?)) | ||||||
|  | 
 | ||||||
|  |  (define (rada-ml-pred? mr) | ||||||
|  |    (or (brmember-council? mr) | ||||||
|  |        (brmember-chair? mr) | ||||||
|  |        (brmember-revision? mr))) | ||||||
|  | 
 | ||||||
|  |  ) | ||||||
|  | @ -30,6 +30,7 @@ | ||||||
|  ( |  ( | ||||||
|   lookup-member-fee |   lookup-member-fee | ||||||
|   member-calendar |   member-calendar | ||||||
|  |   make-member-calendar-entry | ||||||
|   member-calendar-first-month |   member-calendar-first-month | ||||||
|   member-calendar-last-month |   member-calendar-last-month | ||||||
|   member-calendar-query |   member-calendar-query | ||||||
|  | @ -40,12 +41,14 @@ | ||||||
|   member-calendar->table |   member-calendar->table | ||||||
|   members-summary |   members-summary | ||||||
|   member-calendar-entry->fee |   member-calendar-entry->fee | ||||||
|  |   get-expected-income-string | ||||||
|   ) |   ) | ||||||
| 
 | 
 | ||||||
|  (import scheme |  (import scheme | ||||||
| 	 (chicken base) | 	 (chicken base) | ||||||
| 	 (chicken format) | 	 (chicken format) | ||||||
| 	 (chicken sort) | 	 (chicken sort) | ||||||
|  | 	 (chicken string) | ||||||
| 	 srfi-1 | 	 srfi-1 | ||||||
| 	 configuration | 	 configuration | ||||||
| 	 brmember | 	 brmember | ||||||
|  | @ -82,15 +85,17 @@ | ||||||
|        (if (cal-month>? cm last-month) |        (if (cal-month>? cm last-month) | ||||||
| 	   (reverse cal) | 	   (reverse cal) | ||||||
| 	   (loop (cal-month-add cm) | 	   (loop (cal-month-add cm) | ||||||
| 		 (cons (list cm | 		 (cons (with-current-month | ||||||
| 			     (with-current-month | 			cm | ||||||
| 			      cm | 			(make-member-calendar-entry mr)) | ||||||
| 			      (brmember-flags mr)) |  | ||||||
| 			     (with-current-month |  | ||||||
| 			      cm |  | ||||||
| 			      (brmember-spec-fee mr))) |  | ||||||
| 		       cal)))))) | 		       cal)))))) | ||||||
| 
 | 
 | ||||||
|  |  ;; Assumes current-month is specified correctly | ||||||
|  |  (define (make-member-calendar-entry mr) | ||||||
|  |    (list (*current-month*) | ||||||
|  | 	 (brmember-flags mr) | ||||||
|  | 	 (brmember-spec-fee mr))) | ||||||
|  | 
 | ||||||
|  ;; Returns the first month of the calendar |  ;; Returns the first month of the calendar | ||||||
|  (define (member-calendar-first-month mc) |  (define (member-calendar-first-month mc) | ||||||
|    (caar mc)) |    (caar mc)) | ||||||
|  | @ -202,5 +207,26 @@ | ||||||
| 		    (+ (cdr acc) (if (brmember-student? mr) 0 1)))) | 		    (+ (cdr acc) (if (brmember-student? mr) 0 1)))) | ||||||
| 	    (cons 0 0) | 	    (cons 0 0) | ||||||
| 	    members))) | 	    members))) | ||||||
|  | 
 | ||||||
|  |  (define (get-expected-income-string mb) | ||||||
|  |    (let* ((flst | ||||||
|  | 	   (map (compose member-calendar-entry->fee make-member-calendar-entry) | ||||||
|  | 		(find-members-by-predicate mb brmember-active?))) | ||||||
|  | 	  (amts (sort (delete-duplicates flst) <)) | ||||||
|  | 	  (sums | ||||||
|  | 	   (map | ||||||
|  | 	    (lambda (amt) | ||||||
|  | 	      (cons amt | ||||||
|  | 		    (length (filter (lambda (v) (= v amt)) flst)))) | ||||||
|  | 	    amts))) | ||||||
|  |      (string-append | ||||||
|  |       "Expected income: " | ||||||
|  |       (string-intersperse (map | ||||||
|  | 			   (lambda (p) | ||||||
|  | 			     (format "~A*~A" (cdr p) (car p))) | ||||||
|  | 			   sums) | ||||||
|  | 			  " + ") | ||||||
|  |       " = " | ||||||
|  |       (number->string (foldl + 0 (map (lambda (p) (* (car p) (cdr p))) sums)))))) | ||||||
|   |   | ||||||
|  ) |  ) | ||||||
|  |  | ||||||
|  | @ -500,7 +500,17 @@ | ||||||
| 			       (null? (cdr dsa))) | 			       (null? (cdr dsa))) | ||||||
| 			   #f | 			   #f | ||||||
| 			   (cadr dsa)))) | 			   (cadr dsa)))) | ||||||
|      (let* ((members ;; Pass 1 |      (let* ((raw-members | ||||||
|  | 	     (sort | ||||||
|  | 	      (if destroyed? | ||||||
|  | 		  (find-members-by-predicate MB (lambda x #t)) | ||||||
|  | 		  (if only-active? | ||||||
|  | 		      (find-members-by-predicate MB (lambda (mr) | ||||||
|  | 						      (brmember-active? mr))) | ||||||
|  | 		      (find-members-by-predicate MB (lambda (mr) | ||||||
|  | 						      (not (brmember-destroyed? mr)))))) | ||||||
|  | 	      brmember<?)) | ||||||
|  | 	    (members ;; Pass 1 | ||||||
| 	     (map | 	     (map | ||||||
| 	      (lambda (mr) | 	      (lambda (mr) | ||||||
| 		(let* ((balance (member-balance mr)) | 		(let* ((balance (member-balance mr)) | ||||||
|  | @ -522,15 +532,7 @@ | ||||||
| 			total | 			total | ||||||
| 			balance | 			balance | ||||||
| 			))) | 			))) | ||||||
| 	      (sort | 	      raw-members)) | ||||||
| 	       (if destroyed? |  | ||||||
| 		   (find-members-by-predicate MB (lambda x #t)) |  | ||||||
| 		   (if only-active? |  | ||||||
| 		       (find-members-by-predicate MB (lambda (mr) |  | ||||||
| 						       (brmember-active? mr))) |  | ||||||
| 		       (find-members-by-predicate MB (lambda (mr) |  | ||||||
| 						       (not (brmember-destroyed? mr)))))) |  | ||||||
| 	       brmember<?))) |  | ||||||
| 	    (balances (map (lambda (m) | 	    (balances (map (lambda (m) | ||||||
| 			     (list-ref m 6)) | 			     (list-ref m 6)) | ||||||
| 			   members))) | 			   members))) | ||||||
|  | @ -598,19 +600,7 @@ | ||||||
| 		     (map (lambda (member) | 		     (map (lambda (member) | ||||||
| 			    (min 0 (list-ref member 5))) | 			    (min 0 (list-ref member 5))) | ||||||
| 			  members))) | 			  members))) | ||||||
|        (let* ((ns (foldl (lambda (acc member) |        (print (get-expected-income-string MB))))) | ||||||
| 			   (cons (+ (car acc) (if (eq? 'student (cadr member)) 1 0)) |  | ||||||
| 				 (+ (cdr acc) (if (eq? 'active (cadr member)) 1 0)))) |  | ||||||
| 			 (cons 0 0) |  | ||||||
| 			 members)) |  | ||||||
| 	      (students (car ns)) |  | ||||||
| 	      (full (cdr ns))) |  | ||||||
| 	 (print "Expected income: " |  | ||||||
| 		(+ (* (lookup-member-fee 'normal) full) |  | ||||||
| 		   (* (lookup-member-fee 'student) students)) |  | ||||||
| 		" (" full " full members + " students " students)")) |  | ||||||
|        ) |  | ||||||
|      )) |  | ||||||
| 
 | 
 | ||||||
|  (define (unpaired-table mb . args) |  (define (unpaired-table mb . args) | ||||||
|    (apply |    (apply | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
| 	 (chicken format) | 	 (chicken format) | ||||||
| 	 (chicken string) | 	 (chicken string) | ||||||
| 	 (chicken sort) | 	 (chicken sort) | ||||||
|  | 	 (chicken port) | ||||||
| 	 brmember | 	 brmember | ||||||
| 	 util-mail | 	 util-mail | ||||||
| 	 util-bst-ldict | 	 util-bst-ldict | ||||||
|  | @ -54,7 +55,8 @@ | ||||||
| 	 table | 	 table | ||||||
| 	 bank-account | 	 bank-account | ||||||
| 	 logging | 	 logging | ||||||
| 	 srfi-1) | 	 srfi-1 | ||||||
|  | 	 mailinglist) | ||||||
| 
 | 
 | ||||||
|  ;; Prints email to the console |  ;; Prints email to the console | ||||||
|  (define (print-notification-email em) |  (define (print-notification-email em) | ||||||
|  | @ -142,16 +144,14 @@ | ||||||
|      (send-notification-email em))) |      (send-notification-email em))) | ||||||
| 
 | 
 | ||||||
|  ;; Summary email of membership fees payments |  ;; Summary email of membership fees payments | ||||||
|  (define (summary-email-body mb) |  (define (summary-email-body mb mls) | ||||||
|    (let* ((mbs (members-summary mb)) |    (let* ((mbs (members-summary mb)) | ||||||
| 	  (students (car mbs)) | 	  (students (car mbs)) | ||||||
| 	  (full (cdr mbs)) | 	  (full (cdr mbs)) | ||||||
| 	  (income (+ (* (lookup-member-fee 'normal) full) | 	  (income (+ (* (lookup-member-fee 'normal) full) | ||||||
| 		     (* (lookup-member-fee 'student) students))) | 		     (* (lookup-member-fee 'student) students))) | ||||||
| 	  (income-lst | 	  (income-lst | ||||||
| 	   (list (format "Expected income: ~A CZK" income) | 	   (list (get-expected-income-string mb))) | ||||||
| 		 (format "  ~A full members" full) |  | ||||||
| 		 (format "  ~A students" students))) |  | ||||||
| 	  (unpaired (mbase-unpaired mb)) | 	  (unpaired (mbase-unpaired mb)) | ||||||
| 	  (unpaired-lst | 	  (unpaired-lst | ||||||
| 	   (if (null? unpaired) | 	   (if (null? unpaired) | ||||||
|  | @ -160,6 +160,30 @@ | ||||||
| 		(list "" | 		(list "" | ||||||
| 		      "Unpaired transactions:") | 		      "Unpaired transactions:") | ||||||
| 		(unpaired-table mb #:border-style 'ascii)))) | 		(unpaired-table mb #:border-style 'ascii)))) | ||||||
|  | 	  (soonexps (sort | ||||||
|  | 		     (find-members-by-predicate | ||||||
|  | 		      mb | ||||||
|  | 		      (brmember-suspended-for 21 24)) | ||||||
|  | 		     brmember<?)) | ||||||
|  | 	  (soonexps-lst | ||||||
|  | 	   (if (null? soonexps) | ||||||
|  | 	       #f | ||||||
|  | 	       (list "" | ||||||
|  | 		     (format "Expiring members (~A): ~A" | ||||||
|  | 			     (length soonexps) | ||||||
|  | 			     (string-intersperse | ||||||
|  | 			      (map | ||||||
|  | 			       (lambda (mr) | ||||||
|  | 				 (brmember-format "~N (~S)" mr)) | ||||||
|  | 			       soonexps) | ||||||
|  | 			      ","))))) | ||||||
|  | 	  (mlcheck-lst | ||||||
|  | 	   (cons "" | ||||||
|  | 		 (string-split | ||||||
|  | 		  (with-output-to-string | ||||||
|  | 		    (lambda () | ||||||
|  | 		      (print-mailing-list-checks mb mls))) | ||||||
|  | 		  "\n"))) | ||||||
| 	  (debtors (sort | 	  (debtors (sort | ||||||
| 		    (members-to-notify mb 1) | 		    (members-to-notify mb 1) | ||||||
| 		    brmember<?)) | 		    brmember<?)) | ||||||
|  | @ -256,6 +280,8 @@ | ||||||
| 		       ", ")))))) | 		       ", ")))))) | ||||||
|      (append income-lst |      (append income-lst | ||||||
| 	     unpaired-lst | 	     unpaired-lst | ||||||
|  | 	     soonexps-lst | ||||||
|  | 	     mlcheck-lst | ||||||
| 	     debtors-lst | 	     debtors-lst | ||||||
| 	     boring-lst | 	     boring-lst | ||||||
| 	     dw-lst | 	     dw-lst | ||||||
|  | @ -266,21 +292,21 @@ | ||||||
| 		   )))) | 		   )))) | ||||||
| 
 | 
 | ||||||
|  ;; Creates the summary email structure |  ;; Creates the summary email structure | ||||||
|  (define (make-summary-email mb) |  (define (make-summary-email mb mls) | ||||||
|    (make-ldict |    (make-ldict | ||||||
|     `((to . ,(*summary-mailto*)) |     `((to . ,(*summary-mailto*)) | ||||||
|       (subject . ,(format "Členské příspěvky ~A" |       (subject . ,(format "Členské příspěvky ~A" | ||||||
| 			  (today/iso))) | 			  (today/iso))) | ||||||
|       (body . ,(summary-email-body mb))))) |       (body . ,(summary-email-body mb mls))))) | ||||||
| 
 | 
 | ||||||
|  ;; Just print to standard output |  ;; Just print to standard output | ||||||
|  (define (make+print-summary-email mb) |  (define (make+print-summary-email mb mls) | ||||||
|    (let ((em (make-summary-email mb))) |    (let ((em (make-summary-email mb mls))) | ||||||
|      (print-notification-email em))) |      (print-notification-email em))) | ||||||
| 
 | 
 | ||||||
|  ;; Actually send emails |  ;; Actually send emails | ||||||
|  (define (make+send-summary-email mr) |  (define (make+send-summary-email mr mls) | ||||||
|    (let ((em (make-summary-email mr))) |    (let ((em (make-summary-email mr mls))) | ||||||
|      (send-notification-email em))) |      (send-notification-email em))) | ||||||
| 
 | 
 | ||||||
|  ) |  ) | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ | ||||||
| 	 (chicken format)) | 	 (chicken format)) | ||||||
| 
 | 
 | ||||||
|  ;; Short banner |  ;; Short banner | ||||||
|  (define banner-line "HackerBase 1.15.1 (c) 2023 Brmlab, z.s.") |  (define banner-line "HackerBase 1.16.2 (c) 2023-2024 Brmlab, z.s.") | ||||||
| 
 | 
 | ||||||
|  ;; Banner source with numbers for ANSI CSI SGR |  ;; Banner source with numbers for ANSI CSI SGR | ||||||
|  (define banner-source " |  (define banner-source " | ||||||
|  |  | ||||||
|  | @ -286,10 +286,12 @@ | ||||||
|        (call/cc |        (call/cc | ||||||
| 	(lambda (cc) | 	(lambda (cc) | ||||||
| 	  (set! break cc) | 	  (set! break cc) | ||||||
| 	  (if resume | 	  (cond (resume | ||||||
| 	      (resume '()) | 		 (resume '()) | ||||||
| 	      (bst-iter-kv bst yield)) | 		 (break #f)) | ||||||
| 	  #f))))) | 		(else | ||||||
|  | 		 (bst-iter-kv bst yield) | ||||||
|  | 		 (break #f)))))))) | ||||||
| 
 | 
 | ||||||
|  (define/doc (bst-keys bst) |  (define/doc (bst-keys bst) | ||||||
|    ("Returns all the keys contained in given dictionary.") |    ("Returns all the keys contained in given dictionary.") | ||||||
|  |  | ||||||
|  | @ -39,11 +39,12 @@ member file parsers. All functions are UTF-8 aware.") | ||||||
|   |   | ||||||
|  (import scheme |  (import scheme | ||||||
| 	 (chicken base) | 	 (chicken base) | ||||||
|  | 	 racket-kwargs | ||||||
| 	 testing) | 	 testing) | ||||||
| 
 | 
 | ||||||
|  ;; Pass 0: Removes any comments and removes any leading and trailing |  ;; Pass 0: Removes any comments and removes any leading and trailing | ||||||
|  ;; whitespace. |  ;; whitespace. | ||||||
|  (define/doc (parser-preprocess-line line) |  (define*/doc (parser-preprocess-line line #:strip-comments? (strip-comments? #t)) | ||||||
|    ("* ```line``` - a string with contents of one source line |    ("* ```line``` - a string with contents of one source line | ||||||
| 
 | 
 | ||||||
| If the input ```line``` contains the ```#``` character, the rest of | If the input ```line``` contains the ```#``` character, the rest of | ||||||
|  | @ -62,7 +63,9 @@ Returns a string representing the preprocessed line.") | ||||||
| 		      (ploop (add1 pidx))))) | 		      (ploop (add1 pidx))))) | ||||||
| 	  (hpos (let hloop ((hidx ppos)) | 	  (hpos (let hloop ((hidx ppos)) | ||||||
| 		  (if (or (= hidx llen) | 		  (if (or (= hidx llen) | ||||||
| 			  (eq? (string-ref line hidx) #\#)) | 			  (and (or strip-comments? | ||||||
|  | 				   (= hidx 0)) | ||||||
|  | 			       (eq? (string-ref line hidx) #\#))) | ||||||
| 		      hidx | 		      hidx | ||||||
| 		      (hloop (add1 hidx))))) | 		      (hloop (add1 hidx))))) | ||||||
| 	  (spos (let sloop ((sidx (sub1 hpos))) | 	  (spos (let sloop ((sidx (sub1 hpos))) | ||||||
|  |  | ||||||
|  | @ -120,13 +120,13 @@ of the string and a list of remaining bytes (as integers).") | ||||||
|  (define/doc (utf8-bytes->lists chars) |  (define/doc (utf8-bytes->lists chars) | ||||||
|    ("The same as above but accepts a list of bytes (as integers).") |    ("The same as above but accepts a list of bytes (as integers).") | ||||||
|    (let loop ((bytes chars) |    (let loop ((bytes chars) | ||||||
| 	      (rpending '()) | 	      (rpending chars) | ||||||
| 	      (pending 0) | 	      (pending 0) | ||||||
| 	      (expected #f) | 	      (expected #f) | ||||||
| 	      (res '())) | 	      (res '())) | ||||||
|      (if (null? bytes) |      (if (null? bytes) | ||||||
| 	 (values (reverse res) | 	 (values (reverse res) | ||||||
| 		 (reverse rpending)) | 		 rpending) | ||||||
| 	 (let ((byte (car bytes))) | 	 (let ((byte (car bytes))) | ||||||
| 	   (cond (expected | 	   (cond (expected | ||||||
| 		  ;; Decode UTF-8 sequence | 		  ;; Decode UTF-8 sequence | ||||||
|  | @ -135,14 +135,14 @@ of the string and a list of remaining bytes (as integers).") | ||||||
| 			 (let ((char (integer->char (bitwise-ior pending | 			 (let ((char (integer->char (bitwise-ior pending | ||||||
| 								 (bitwise-and byte #b111111))))) | 								 (bitwise-and byte #b111111))))) | ||||||
| 			   (loop (cdr bytes) | 			   (loop (cdr bytes) | ||||||
| 				 '() | 				 (cdr bytes) | ||||||
| 				 0 | 				 0 | ||||||
| 				 #f | 				 #f | ||||||
| 				 (cons char res)))) | 				 (cons char res)))) | ||||||
| 			(else | 			(else | ||||||
| 			 ;; Intermediate bytes | 			 ;; Intermediate bytes | ||||||
| 			 (loop (cdr bytes) | 			 (loop (cdr bytes) | ||||||
| 			       (cons byte rpending) | 			       rpending | ||||||
| 			       (arithmetic-shift (bitwise-ior pending | 			       (arithmetic-shift (bitwise-ior pending | ||||||
| 							      (bitwise-and byte #b111111)) 6) | 							      (bitwise-and byte #b111111)) 6) | ||||||
| 			       (sub1 expected) | 			       (sub1 expected) | ||||||
|  | @ -152,7 +152,7 @@ of the string and a list of remaining bytes (as integers).") | ||||||
| 		  (cond ((= (bitwise-and byte #b10000000) 0) | 		  (cond ((= (bitwise-and byte #b10000000) 0) | ||||||
| 			 ;; ASCII | 			 ;; ASCII | ||||||
| 			 (loop (cdr bytes) | 			 (loop (cdr bytes) | ||||||
| 			       '() | 			       (cdr bytes) | ||||||
| 			       0 | 			       0 | ||||||
| 			       #f | 			       #f | ||||||
| 			       (cons (integer->char byte) res))) | 			       (cons (integer->char byte) res))) | ||||||
|  | @ -160,20 +160,20 @@ of the string and a list of remaining bytes (as integers).") | ||||||
| 			 ;; First byte of UTF-8 sequence | 			 ;; First byte of UTF-8 sequence | ||||||
| 			 (let-values | 			 (let-values | ||||||
| 			     (((first-byte char-bytes) | 			     (((first-byte char-bytes) | ||||||
| 			       (cond ((= (bitwise-and byte #b11000000) #b11000000) | 			       (cond ((= (bitwise-and byte #b11100000) #b11000000) | ||||||
| 				      (values (bitwise-and byte #b11111) | 				      (values (bitwise-and byte #b11111) | ||||||
| 					      2)) | 					      2)) | ||||||
| 				     ((= (bitwise-and byte #b11100000) #b11100000) | 				     ((= (bitwise-and byte #b11110000) #b11100000) | ||||||
| 				      (values (bitwise-and byte #b1111) | 				      (values (bitwise-and byte #b1111) | ||||||
| 					      3)) | 					      3)) | ||||||
| 				     ((= (bitwise-and byte #b11110000) #b11110000) | 				     ((= (bitwise-and byte #b11111000) #b11110000) | ||||||
| 				      (values (bitwise-and byte #b111) | 				      (values (bitwise-and byte #b111) | ||||||
| 					      4)) | 					      4)) | ||||||
| 				     (else | 				     (else | ||||||
| 				      ;; Should not happen | 				      ;; Should not happen | ||||||
| 				      (values 0 0))))) | 				      (values 0 0))))) | ||||||
| 			   (loop (cdr bytes) | 			   (loop (cdr bytes) | ||||||
| 				 (list byte) | 				 bytes | ||||||
| 				 (arithmetic-shift first-byte 6) | 				 (arithmetic-shift first-byte 6) | ||||||
| 				 (sub1 char-bytes) | 				 (sub1 char-bytes) | ||||||
| 				 res)))))))))) | 				 res)))))))))) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue