/* * * sleep.c - plots a sleep diary * version 0.5 * (c) 2001 Steve G Parker (steve@steve-parker.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * With thanks to Peter (peter-is-my-name@home.com) for sorting me out on * Variance, and the need to divide it by 60^2. * Files Required: diary.dat, sleep.dat, other.dat diary.dat eg: Fri 11 May 2 Means: "Friday 11th May - overall feeling is 2/5" sleep.dat eg: 26 Jun 00 45 26 Jun 04 00 26 Jun 09 15 Or: dd mmm hh mm x3 1st = Try to sleep 2nd = Get to sleep 3rd = Wake up other.dat eg: drink 23 Jul 23 00 24 Jul 07 00 eg: 95work 23 Jul 09 00 23 Jul 17 30 eg: work 23 Jul 12 00 23 Jul 18 30 Or: type dd mmm hh mm dd mmm hh mm Where: type is the type of activity (and gif/type.gif exists) dd mmm is the date hh mm is the start (or finish) time of the activity. */ #include #include #include #include struct time_struct { int h; int m; }; struct date_struct { char dom[5]; int d; char mon[5]; int m; }; struct tod_struct { struct date_struct d; struct time_struct time; }; struct sleep_struct { struct tod_struct try; struct tod_struct sleep; struct tod_struct wake; struct sleep_struct *next; struct sleep_struct *prev; }; struct other_struct { char *name; struct tod_struct start; struct tod_struct finish; struct other_struct *next; struct other_struct* prev; char *comment; }; struct diary_struct { struct date_struct date; int overall; char *feel; char *comments; struct sleep_struct *sleep; struct other_struct *other; struct diary_struct *next; struct diary_struct *prev; }; struct week_struct { struct diary_struct *first_day; struct week_struct *next; int work; int idx; }; #define RATIO 0.33 #define MAXENTRIES 1000 // Is this necessary? It's just for the stats array... #define ACT_TRY 0 // These are for the #define ACT_SLEEP 1 // stats array #define ACT_WAKE 2 int stats[3][MAXENTRIES]; struct diary_struct *first_entry; struct week_struct *first_week; struct week_struct *thisweek; char summary[1024]; char tmp_summ[255]; int sleep_today; FILE *html; void krqsort(int v[], int left, int right) { /* qsort from K&R; renamed from qsort as glibc already has a qsort */ int i, last, x, y; if (left>=right) return; x=(left+right)/2; y=v[left]; v[left]=v[x]; v[x]=y; last=left; for (i=left+1; i <= right; i++) if (v[i] < v[left]) { last++; y=v[last]; v[last]=v[i]; v[i]=y; } y=v[left]; v[left]=v[last]; v[last]=y; krqsort(v, left, last-1); krqsort(v, last+1, right); } void sort_stats(int idx) { int i; for (i=0; i<3; i++) { krqsort(stats[i], 0, idx); } } void m2mh(int min, char *s) { int m,h; m=(min % 60); h=(min / 60); sprintf(s, "%02d:%02d", h, m); } void show_stats(int idx) { int i,j,c,mode; char t[10]; int mean; double variance=0.0; double standarddeviation=0.0; /* // This shows the raw data, be it sorted or unsorted. fprintf(html, "\n"); for (i=0; i<3; i++) { fprintf(html, ""); for (j=0; j<=idx; j++) { fprintf(html, "", stats[i][j]); } fprintf(html, "\n"); } fprintf(html, "
%d
\n"); */ fprintf(html, "\n"); fprintf(html, "\n", idx); i=0; while(stats[ACT_TRY][i]==0) i++; m2mh(stats[ACT_TRY][i], t); fprintf(html, "", t); m2mh(stats[ACT_TRY][idx], t); fprintf(html, "", t); // calc mean here for (i=0; i<=idx; i++) { c+=stats[ACT_TRY][i]; } c=(c/idx); mean=c; m2mh(c, t); fprintf(html, "", t); // calc mode here if ((idx%2) == 1) mode=stats[ACT_TRY][idx/2]; else { c=idx/2; // this rounds up... mode=(stats[ACT_TRY][c] + stats[ACT_TRY][c+1]) /2; } m2mh(mode, t); fprintf(html, "\n", t); /* This is the start of the Variance and Std Deviation bit SD is the sqrt of the Variance. http://davidmlane.com/hyperstat/A16252.html http://davidmlane.com/hyperstat/A40397.html */ variance=0.0; for (i=0; i%s\n", t); m2mh(sqrt(variance), t); fprintf(html, "\n", t); /* This is the end of the Variance and Std Deviation bit */ i=0; while(stats[ACT_SLEEP][i]==0) i++; m2mh(stats[ACT_SLEEP][i], t); fprintf(html, "", t); m2mh(stats[ACT_SLEEP][idx], t); fprintf(html, "", t); // calc mean here for (i=0; i<=idx; i++) { c+=stats[ACT_SLEEP][i]; } c=(c/idx); mean=c; m2mh(c, t); fprintf(html, "", t); if ((idx%2) == 1) mode=stats[ACT_SLEEP][idx/2]; else { c=idx/2; // this rounds up... mode=(stats[ACT_SLEEP][c] + stats[ACT_SLEEP][c+1]) /2; } m2mh(mode, t); fprintf(html, "\n", t); variance=0.0; for (i=0; i%s\n", t); m2mh(sqrt(variance), t); fprintf(html, "\n", t); fprintf(html, "\n"); i=0; while(stats[ACT_WAKE][i]==0) i++; m2mh(stats[2][i], t); fprintf(html, "", t); m2mh(stats[2][idx], t); fprintf(html, "", t); // calc mean here for (i=0; i<=idx; i++) { c+=stats[ACT_WAKE][i]; } c=(c/idx); m2mh(c, t); fprintf(html, "", t); if ((idx%2) == 1) mode=stats[ACT_WAKE][idx/2]; else { c=idx/2; // this rounds up... mode=(stats[ACT_WAKE][c] + stats[ACT_WAKE][c+1]) /2; } m2mh(mode, t); fprintf(html, "\n", t); variance=0.0; for (i=0; i%s\n", t); m2mh(sqrt(variance), t); fprintf(html, "\n", t); fprintf(html, "\n"); fprintf(html, "
Stats (%d days)MinMaxMeanModeVarianceSD
Trying%s%s%s%s%s
Sleeping%s%s%s%s%s
Awake%s%s%s%s%s
\n"); } int same_day(struct date_struct d1, struct date_struct d2) { if ( (d1.d == d2.d) && (!strcmp(d1.mon, d2.mon)) ) { return 1; } else { return 0; } } void show_other(struct other_struct *o, struct diary_struct *d) { int width, m1, m2; if ( (!same_day(o->start.d, d->date)) && (!same_day(o->finish.d, d->date)) ) return; /* Don't want to add text on non-sleep items. if (same_day(o->start.d, d->date)) strcat(summary, o->comment); */ if (same_day(o->start.d, d->date)) { m1=o->start.time.h*60 + o->start.time.m; width=m1*RATIO; if (m1>0) if ( (o->prev==NULL) || (strcmp(o->prev->name, o->name)!=0) ) fprintf(html, "\"\"", width); } else m1=0; if (same_day(o->finish.d, d->date)) m2=o->finish.time.h*60 + o->finish.time.m; else m2=24*60; width=m2-m1; width=width*RATIO; if (!strcmp(o->name, "actual.work")) { thisweek->work+=(m2-m1); } if (width>0) fprintf(html, "\"%s\"", o->name, o->comment, width); if ( (o->next!=NULL) && (same_day(o->next->start.d, d->date)) ) { //printf("\no->next=%s\n", o->next->name); if (!strcmp(o->name, o->next->name)) { // We'll add any gaps it needs... m1=m2; if (same_day(o->next->start.d, d->date)) m2=o->next->start.time.h*60 + o->next->start.time.m; else m2=24*60; width=m2-m1; width=width*RATIO; if (width>0) fprintf(html, "\"\"", width); } else fprintf(html, "
\n"); } else fprintf(html, "
\n"); } void show_sleep(struct sleep_struct *s, struct diary_struct *d) { int width, m1, m2; strcat(summary, ""); // TRY if (same_day(s->try.d, d->date)) m1=s->try.time.h*60 + s->try.time.m; else m1=0; if (same_day(s->sleep.d, d->date)) m2=s->sleep.time.h*60 + s->sleep.time.m; else m2=24*60; width=m2-m1; width=width*RATIO; if ( (s->prev==NULL) && (m1>0) ) { int w=m1*RATIO; fprintf(html, "\"wake", s->try.time.h, s->try.time.m, w); } if ( (width>0) && ( (same_day(s->try.d, d->date)) || (same_day(s->sleep.d, d->date))) ) { if (same_day(s->try.d, d->date)) { sprintf(tmp_summ, "Try: %02d:%02d", s->try.time.h, s->try.time.m); strcat(summary, tmp_summ); } fprintf(html, "\"try", s->try.time.h, s->try.time.m, m2/60, m2%60, width); } // SLEEP if (same_day(s->sleep.d, d->date)) { m1=s->sleep.time.h*60; m1+=s->sleep.time.m; } else m1=0; if (same_day(s->wake.d, d->date)) m2=s->wake.time.h*60 + s->wake.time.m; else m2=24*60; width=m2-m1; width=width*RATIO; if ( (width>0) && ( (same_day(s->sleep.d, d->date)) || (same_day(s->wake.d, d->date))) ) { if (same_day(s->sleep.d, d->date)) { sprintf(tmp_summ, " Sleep: %02d:%02d", s->sleep.time.h, s->sleep.time.m); sleep_today+=(m2-m1); strcat(summary, tmp_summ); } fprintf(html, "\"asleep", s->sleep.time.h, s->sleep.time.m, m2/60, m2%60, width); } // WAKE if (same_day(s->wake.d, d->date)) m1=s->wake.time.h*60 + s->wake.time.m; else m1=0; if ( (s->next!=NULL) && (same_day(s->next->try.d, d->date)) ) m2=s->next->try.time.h*60 + s->next->try.time.m; else m2=24*60; width=m2-m1; width=width*RATIO; if (width>0) if (s->next!=NULL) { if ( (same_day(s->wake.d, d->date)) || (same_day(s->next->try.d, d->date)) ) { sprintf(tmp_summ, " Wake: %02d:%02d", s->wake.time.h, s->wake.time.m); strcat(summary, tmp_summ); fprintf(html, "\"awake", s->wake.time.h, s->wake.time.m, m2/60, m2%60, width); } } else if (same_day(s->wake.d, d->date)) { sprintf(tmp_summ, " Wake: %02d:%02d", s->wake.time.h, s->wake.time.m); strcat(summary, tmp_summ); fprintf(html,"\"awake", s->wake.time.h, s->wake.time.m, m2/60, m2%60, width); } } void show_day(struct diary_struct *d) { struct other_struct *o; struct sleep_struct *s; char colour[100]; char overall[100]; sprintf(summary, ""); if ( (!strcmp(d->date.dom, "Sat")) || (!strcmp(d->date.dom, "Sun")) ) sprintf(colour, " bgcolor=\"silver\""); else sprintf(colour, ""); fprintf(html, "%s %d %s\n", colour, d->date.dom, d->date.d, d->date.mon); fprintf(html, " "); o=d->other; while (o!=NULL) { show_other(o, d); o=o->next; } s=d->sleep; while (s!=NULL) { show_sleep(s, d); s=s->next; } sprintf(overall, "%d", d->overall); fprintf(html, "
\n
%sTotal Sleep: %02d:%02d
", summary, sleep_today/60, sleep_today%60); fprintf(html, "\n \n"); fprintf(html, " %d%s \n", d->overall, overall, d->feel); fprintf(html, " %s \n", d->comments); fprintf(html, "\n"); } char *get_filename(struct week_struct *w) { char *f=(char*)malloc(255); sprintf(f, "sleep-diary-wc-%s.%d.%s.html", w->first_day->date.dom, w->first_day->date.d, w->first_day->date.mon); return f; } void show_weeks(struct week_struct *tw) { struct week_struct *w=first_week; char this_month[25]; sprintf(this_month, "%s", w->first_day->date.mon); fprintf(html, "\n
\n"); fprintf(html, "\n\n"); fprintf(html, "", w->first_day->date.mon); while (w!=NULL) { if (strcmp(this_month, w->first_day->date.mon)) fprintf(html, "\n", w->first_day->date.mon); sprintf(this_month, "%s", w->first_day->date.mon); fprintf(html, "\n"); if (w->next==NULL) fprintf(html, " "); w=w->next; } fprintf(html, "\n
%s
%s"); /* if (tw!=w) fprintf(html, "", w->first_day->date.dom, w->first_day->date.d, w->first_day->date.mon); else fprintf(html, ""); fprintf(html, "%s %d %s", w->first_day->date.dom, w->first_day->date.d, w->first_day->date.mon); if (tw!=w) fprintf(html, ""); else fprintf(html, ""); */ fprintf(html, "", w->first_day->date.dom, w->first_day->date.d, w->first_day->date.mon); fprintf(html, "%s %d %s", w->first_day->date.dom, w->first_day->date.d, w->first_day->date.mon); fprintf(html, ""); fprintf(html, "
\n
\n"); } void show_results(int idx) { struct diary_struct *d=first_entry; char *filename; int w; FILE *key; char s[255]; w=(24*60*RATIO)+10; thisweek=first_week; filename=get_filename(thisweek); html=fopen(filename, "w"); fprintf(html, "\n\n"); //show_weeks(thisweek); fprintf(html, "\n"); fprintf(html, "\n", w, w); while (d!=NULL) { sleep_today=0; if ( (thisweek->next!=NULL) && (d==thisweek->next->first_day) ) { fprintf(html, "
DateSleep
1-5How Feel on WakingComments
\n"); if (thisweek->work>0) fprintf(html, "Total Work this week (target 37.5h): %02d:%02dh\n", thisweek->work/60, thisweek->work%60); /* sort_stats(idx); show_stats(idx); show_weeks(thisweek); */ fprintf(html, "\n\n"); thisweek=thisweek->next; fclose(html); filename=get_filename(thisweek); html=fopen(filename, "w"); fprintf(html, "\n\n"); //show_weeks(thisweek); fprintf(html, "\n"); fprintf(html, "\n", w, w); } show_day(d); d=d->next; } fprintf(html, "
DateSleep
1-5How Feel on WakingComments
\n"); fprintf(html, "Total Work this week (target 37.5h): %02d:%02dh\n", thisweek->work/60, thisweek->work%60); /* sort_stats(idx); show_stats(idx); show_weeks(thisweek); */ fprintf(html, "\n\n"); fclose(html); html=fopen("index.html", "w"); fprintf(html, "\n\n"); fprintf(html, "

Steve Parker's Sleep Diary - Table of Contents

\n"); show_weeks(thisweek); sort_stats(idx); show_stats(idx); key=fopen("code/key.html", "r"); while (fgets(s, 255, key)) fprintf(html, "%s", s); fclose(key); fprintf(html, "\n\n"); fclose(html); } struct diary_struct *read_diary(char *s, char *t, char *u) { struct diary_struct *d=(struct diary_struct*)malloc(sizeof(struct diary_struct)); struct week_struct *w; sscanf(s, "%s %d %s %d", d->date.dom, &d->date.d, d->date.mon, &d->overall); d->feel=(char*)malloc(strlen(t)+1); sprintf(d->feel, "%s", t); d->comments=(char*)malloc(strlen(u)+1); sprintf(d->comments, "%s", u); d->other=NULL; d->next=NULL; if ( (first_week==NULL) || (!strcmp(d->date.dom, "Mon")) ) { if (first_week==NULL) { thisweek=(struct week_struct *)malloc(sizeof(struct week_struct)); first_week=thisweek; } else { w=(struct week_struct *)malloc(sizeof(struct week_struct)); thisweek->next=w; thisweek=w; } thisweek->first_day = d; } return d; } int timediff(struct tod_struct from, struct tod_struct till, char *x) { // return time in minutes between the two events... int res=0; int h, m; int tth; struct diary_struct *d=first_entry; tth=till.time.h; h=till.time.h - from.time.h; m=till.time.m - from.time.m; /* if (h<0) h+=24; if (m<0) h+=1; */ if (!same_day(from.d, till.d)) { while (!same_day(d->date, from.d)) d=d->next; while (!same_day(d->date, till.d)) { h+=24; d=d->next; } } res=(h*60)+m; /* printf("%-10s: %02d/%s @ %02d:%02d - %02d/%s @ %02d:%02d = %02d:%02d (%d)\n", x, from.d.d, from.d.mon, from.time.h, from.time.m, till.d.d, till.d.mon, till.time.h, till.time.m, res/60, res%60, res); */ return res; } struct sleep_struct *read_sleep(char *s, int idx, struct sleep_struct *prev) { struct sleep_struct *sl=(struct sleep_struct*)malloc(sizeof(struct sleep_struct)); sscanf(s, "%d %s %d %d %d %s %d %d %d %s %d %d", &sl->try.d.d, sl->try.d.mon, &sl->try.time.h, &sl->try.time.m, &sl->sleep.d.d, sl->sleep.d.mon, &sl->sleep.time.h, &sl->sleep.time.m, &sl->wake.d.d, sl->wake.d.mon, &sl->wake.time.h, &sl->wake.time.m); if (idx>0) stats[ACT_WAKE][idx-1]=timediff(prev->wake, sl->try, "wake"); stats[ACT_TRY][idx]=timediff(sl->try, sl->sleep, "try"); stats[ACT_SLEEP][idx]=timediff(sl->sleep, sl->wake, "sleep"); return sl; } struct other_struct *read_other(char *s) { int res; char altstr[255]; struct other_struct *o; o=(struct other_struct*)malloc(sizeof(struct other_struct)); if (o==NULL) { printf("Error in malloc()\n"); } (char*)o->name=(char*)malloc(100); if (o->name==NULL) printf("Error in malloc name\n"); (char*)o->comment=(char*)malloc(100); if (o->comment==NULL) printf("Error in malloc comment\n"); res=sscanf(s, "%s %d %s %d %d %d %s %d %d %100c", o->name, &o->start.d.d, o->start.d.mon, &o->start.time.h, &o->start.time.m, &o->finish.d.d, o->finish.d.mon, &o->finish.time.h, &o->finish.time.m, o->comment); if (res==9) { sprintf(altstr, " %s: %02d:%02d to %02d:%02d", o->name, o->start.time.h, o->start.time.m, o->finish.time.h, o->finish.time.m); o->comment=(char*)strdup(altstr); } else { sprintf(altstr, "%s: %s", o->name, o->comment); sprintf(o->comment, "%s", altstr); // Now strip the trailing \n to keep the HTML tidy... o->comment[strlen(o->comment)-1]='\0'; //strcat(o->comment, ""); } return o; } void position_sleep(struct sleep_struct *sl) { struct diary_struct *d; d=first_entry; while (d!=NULL) { /* Note: Yes, we can put a sleep entry into multiple diary entries. That way, each diary entry can just ignore entries which don't refer to it. */ if ( (same_day(d->date, sl->try.d)) || (same_day(d->date, sl->sleep.d)) || (same_day(d->date, sl->wake.d)) ) { if (d->sleep==NULL) d->sleep=sl; else { struct sleep_struct *sl2=d->sleep; while (sl2->next!=NULL) sl2=sl2->next; sl2->next=sl; sl->prev=sl2; } } d=d->next; } } void position_other(struct other_struct *o) { struct diary_struct *d; d=first_entry; while (d!=NULL) { /* Note: Yes, we can put an entry into multiple diary entries. That way, each diary entry can just ignore entries which don't refer to it. */ if ( (same_day(d->date, o->start.d)) || (same_day(d->date, o->finish.d)) ) { if (d->other==NULL) { d->other=o; // printf("%d %s -> other = %s\n", d->date.d, d->date.mon, o->name); } else { struct other_struct *o2=d->other; while (o2->next!=NULL) { //printf("%s %2d %s @ %02d:%02d\n", o2->name, o2->start.d.d, o2->start.d.mon, o2->start.time.h, o2->start.time.m); o2=o2->next; } if (o2!=o) { // o2 could be o because of the way we're adding these regardless of the start date. //printf("%d %s : %s (%d) %02d:%02d -> next = %s (%d) %02d:%02d\n", d->date.d, d->date.mon, o2->name, o2->start.d.d, o2->start.time.h, o2->start.time.m, o->name, o->start.d.d, o->start.time.h, o->start.time.m); o2->next=o; o->prev=o2; } } } d=d->next; } } int main() { FILE *sleep_dat; FILE *other_dat; FILE *diary_dat; char s[1024]; char t[1024]; char u[1024]; struct diary_struct *d=NULL; struct diary_struct *prev_d=NULL; struct sleep_struct *sl=NULL; struct other_struct *o=NULL; int idx=0; diary_dat=fopen("dat/diary.dat", "r"); while (fgets(s, 1024, diary_dat)) { fgets(t, 1024, diary_dat); fgets(u, 1024, diary_dat); d=read_diary(s, t, u); if (first_entry==NULL) { first_entry=d; } d->prev=prev_d; if (prev_d!=NULL) prev_d->next=d; d->next=NULL; prev_d=d; } fclose(diary_dat); sleep_dat=fopen("dat/sleep.dat", "r"); while (fgets(s, 1024, sleep_dat)) { sl=read_sleep(s, idx, sl); position_sleep(sl); idx++; } fclose(sleep_dat); other_dat=fopen("dat/other.dat", "r"); while (fgets(s, 1024, other_dat)) { o=read_other(s); position_other(o); } fclose(other_dat); show_results(idx); return 0; }