diff --git a/+ww/Params.m b/+ww/Params.m index 84a5617..976634b 100644 --- a/+ww/Params.m +++ b/+ww/Params.m @@ -98,6 +98,7 @@ function save(obj) ... Preferences ... 'Mode', 2, ... 'nDaysInFuture', 2, ... + 'minLastSent', 2, ... 'Email_format', 'html', ... 'Email_admins', {''}, ... 'Email_recipients', {''}); diff --git a/+ww/formatTable.m b/+ww/formatTable.m index ee92b31..6859435 100644 --- a/+ww/formatTable.m +++ b/+ww/formatTable.m @@ -9,6 +9,8 @@ % format (char): Formats include 'png', 'tsv', 'html'. If no format is % specified, a tab separated string is returned. If % 'tsv' a file is also saved to disk. +% +% TODO Improve debugging mode if nargin == 1; format = ''; end dataInCells = table2cell(data); @@ -56,7 +58,9 @@ end out = [out, '']; %%% FOR DEBUGGING %%% - save printWeekendWaterVars.mat + if ww.Params().get('Mode') > 0 + save(fullfile(userpath, 'printWeekendWaterVars.mat')) + end %%% otherwise columnHeaders = cellfun(@strtrim,columnHeaders,'uni',0); diff --git a/+ww/generate.m b/+ww/generate.m index fe95d83..f1bb88b 100644 --- a/+ww/generate.m +++ b/+ww/generate.m @@ -15,12 +15,16 @@ function generate() if isempty(fieldnames(excl)), excl = struct; end % Path to email file which will be sent -mail = fullfile(iff(ispc, getenv('APPDATA'), getenv('HOME')), 'mail.txt'); -% mod = file.modDate(mail); -% Check if email was generated in the past 2 days -% if ~isempty(mod) && (now - file.modDate(which('dat.paths')) < 2) -% return -% end +filename = sprintf('mail%s.txt', iff(test, '-test', '')); +mail = fullfile(iff(ispc, getenv('APPDATA'), getenv('HOME')), filename); + +% Check when email was last generated and potentially return if too soon +mod = file.modDate(mail); +minLastSent = params.get('minLastSent'); % min number of days before next +if ~test && ~isempty(mod) && (now - mod < minLastSent) + fprintf('Email already sent in last %.2g days\n', minLastSent) + return +end % Set email prefs for sending the email % TODO These may no longer be required as we use curl @@ -35,7 +39,7 @@ function generate() end % use 2 for usual weekends, 3 for long weekends etc. -[nDays, fail] = getNumDays(); +[nDays, fail] = ww.getNumDays(); if fail && ~test sendmail(admins, 'Action required: Days may be incorrect',... ['Weekend water script failed to determine whether there are any upcoming ',... @@ -54,7 +58,7 @@ function generate() users = ai.getData('users'); % Extract the data from alyx and give water to whomever needs it -[data, skipped] = getWeekendWater(ai, nDays, excl); +[data, skipped] = ww.getWeekendWater(ai, nDays, excl); if height(data) == 0, return, end % Return if there are no restricted mice if ~isempty(skipped) && ~test @@ -68,12 +72,12 @@ function generate() end % print nicely, 'water.png' will be saved in the current folder -data = formatTable(data, params.get('Email_format')); +data = ww.formatTable(data, params.get('Email_format')); % Get list of email recipients recipients = strip(lower(params.get('Email_recipients'))); % In test mode only send to admin, otherwise send to all recipients and admins -to = iff(test, admins{1}, union(recipients, admins)); +to = iff(test, admins(1), union(recipients, admins)); %% 'Weekend water',... % Write email to file @@ -105,9 +109,11 @@ function generate() gitExe = getOr(dat.paths, 'gitExe'); bashPath = fullfile(gitExe(1:end-11), 'git-bash.exe'); bash = @(cmd)['"',bashPath,'" -c "',cmd,'"']; -system(bash(cmd), '-echo'); % Send email +failed = system(bash(cmd), '-echo'); % Send email + +assert(~failed, 'failed to send email') % Restore previous preferences for prop = string(fieldnames(internetPrefs))' - setpref('Internet', prop, s.(prop)) + setpref('Internet', prop, internetPrefs.(prop)) end \ No newline at end of file diff --git a/+ww/getWeekendWater.m b/+ww/getWeekendWater.m index a22e150..d897675 100644 --- a/+ww/getWeekendWater.m +++ b/+ww/getWeekendWater.m @@ -1,5 +1,6 @@ function [data, skipped] = getWeekendWater(ai, nDaysInFuture, exclude) % WW.GETWEEKENDWATER Builds a table of water amounts and posts to Alyx +% TODO Create log instead of dumping vars to file in debug mode if nargin<2 nDaysInFuture = 2; end @@ -72,7 +73,9 @@ end end %%% FOR DEBUGGING %%% -save getWeekendWaterVars.mat +if ww.Params().get('Mode') > 0 + save(fullfile(userpath, 'getWeekendWaterVars.mat')) +end %%% % collect the data for the table data = table(animalName, prcWeightToday, giveWater); diff --git a/+ww/setup.m b/+ww/setup.m index 4911a93..81d8252 100644 --- a/+ww/setup.m +++ b/+ww/setup.m @@ -3,7 +3,7 @@ % Performs some simple requirements checks then prompts the user to % provide settings and preferences. -% Check for requirements % FIXME Move checks +% Check for requirements assert(~verLessThan('matlab', '9.5'), 'Requires MATLAB 2018b or later') assert(~isempty(which('dat.paths')), ... 'Requires Rigbox', ... @@ -18,6 +18,7 @@ 'https://gitforwindows.org/') params = ww.Params; + % ----STMP-SERVER----- disp('Setting up email STMP server settings...') disp('...Press return to keep unchanged...') @@ -38,6 +39,7 @@ fprintf('Account password: \n'); reply = passwordUI(); if ~isempty(reply), params.set('SMTP_Password', reply); end + % --------ALYX-------- disp('Alyx user account to use...') prompt = sprintf('Alyx username: (%s) ', params.get('ALYX_Login')); @@ -46,6 +48,7 @@ fprintf('Alyx password: \n'); reply = passwordUI(); if ~isempty(reply), params.set('ALYX_Password', reply); end + % ------CALENDAR------ disp('Setting up calandar API settings (for determining bank holidays)...') prompt = sprintf('API URL: (%s) ', params.get('CAL_API_URL')); @@ -60,6 +63,7 @@ prompt = sprintf('Region: (%s) ', params.get('CAL_Region')); reply = input(prompt,'s'); if ~isempty(reply), params.set('CAL_Region', reply); end + % -----PREFERENCES----- disp('Setting up script preferences...') prompt = sprintf('Mode (0 = normal, 1 = debug, test = 2): (%i) ',... @@ -76,6 +80,12 @@ assert(reply > 0) params.set('nDaysInFuture', int8(reply)); end +prompt = sprintf(... + 'Minimum number of days before script can be re-run: (%.2g) ',... + params.get('minLastSent')); +reply = input(prompt); +if ~isempty(reply), params.set('minLastSent', reply); end + % -----EMAILS------ disp('Setting up email recipients...') disp('Please enter a single email in quotes, or a cell array of emails') diff --git a/README.md b/README.md index cad0264..731a166 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ This is designed to work with Rigbox and Alyx. 2. In MATLAB, add the WeekendWater repository to your paths (e.g. `addpath('WeekendWater')`) 3. Follow [these instructions](http://cortex-lab.github.io/Rigbox/paths_config.html) on how to set up your Rigbox paths file. You will need to ensure that the `gitExe` field points to your installed Git Bash executable, and the `mainRepository` is where your parameterProfiles.mat file will be saved (where subjects marked for weekend training are stored). 4. In MATLAB, run `ww.setup` and follow the steps to set the required parameters. -5. To run the script, say, every Friday, create a task on [Windows Scheduler](https://windowsreport.com/schedule-tasks-windows-10/). Add an action to start a program. The program should be MATLAB (i.e. `"C:\Program Files\MATLAB\R2018b\bin\matlab.exe"`) and the arguments should be `-r "ww.generate;exit"`. +5. To run the script, say, every Friday, create a task on [Windows Scheduler](https://windowsreport.com/schedule-tasks-windows-10/). Add an action to start a program. The program should be MATLAB (i.e. `"C:\Program Files\MATLAB\R2018b\bin\matlab.exe"`) and the arguments should be `-r "ww.generate;exit"`*. + +*To ensure that MATLAB returns, even after errors, use `-r "exit(run_safe)"` ## Updating parameters Parameters can be updated using the `ww.Params` class, or by simply re-running `ww.setup`. Below shows how to change the number of future weekend days to 3: diff --git a/run_safe.m b/run_safe.m new file mode 100644 index 0000000..484c863 --- /dev/null +++ b/run_safe.m @@ -0,0 +1,7 @@ +function status = run_safe +try + ww.generate; + status = 0; +catch + status = 1; +end \ No newline at end of file