├── +WEB ├── +API │ ├── Auth.m │ ├── Common.m │ ├── Doc.m │ ├── Examples.m │ ├── README.md │ ├── Req.m │ ├── Update.m │ └── Ver.m ├── +Utils │ ├── Plotter.m │ ├── README.md │ ├── Storage.m │ └── Tables.m ├── BingMaps.m ├── DataGov.m ├── Flickr.m ├── HeadHunter.m ├── IP.m ├── MPS.m ├── NetSuite.m ├── OpenWeatherMap.m ├── README.md ├── RESTCountries.m ├── TelegramBot.m ├── Template.m ├── Uinames.m ├── VK.m └── YouTube.m ├── .gitignore ├── MATLAB-WEB-API.mltbx ├── MATLAB-WEB-API.prj ├── MATLABWEBAPIDev.m ├── MATLABWEBAPIExtender.m ├── MATLABWEBAPIUpdater.m ├── README.md ├── ToolboxConfig.xml ├── cover.png ├── data └── README.md ├── dev_on.m ├── doc ├── Examples.html ├── Examples.mlx ├── GettingStarted.html ├── GettingStarted.mlx ├── MATLAB-WEB-API.png ├── Reference.html ├── Reference.mlx ├── WebApi.html ├── WebApi.mlx ├── WebUtils.html ├── WebUtils.mlx └── helptoc.xml ├── examples ├── bingmaps_example.m ├── datagov_example.m ├── flickr_example.m ├── headhunter_example.m ├── ip_example.m ├── mps_example.m ├── netsuite_example.m ├── openweathermap_example.m ├── req_example.m ├── restcountries_example.m ├── uinames_example.m ├── vk_example.m └── youtube_example.m ├── info.xml ├── install.m └── installweb.m /+WEB/+API/Auth.m: -------------------------------------------------------------------------------- 1 | classdef Auth < handle 2 | % WEB Requests Authenticator 3 | 4 | properties 5 | req % Req Object 6 | authdata % Authentication Data 7 | method % HTTP method 8 | end 9 | 10 | methods 11 | function obj = Auth(req, authdata, method) 12 | %% Create new Auth object 13 | if nargin > 0 14 | obj.req = req; 15 | end 16 | if nargin > 1 17 | obj.authdata = authdata; 18 | end 19 | if nargin > 2 20 | obj.method = method; 21 | end 22 | end 23 | 24 | function req = auth(obj) 25 | %% Authenticate 26 | a = obj.authdata; 27 | if ~isempty(a) && isfield(a, 'oauth_version') 28 | if a.oauth_version == "1.0" 29 | obj.oauth10() 30 | end 31 | end 32 | req = obj.req; 33 | end 34 | 35 | function req = oauth10(obj) 36 | %% OAuth 1.0 37 | a = obj.authdata; 38 | A = obj.newtable(); 39 | for f = fieldnames(a)' 40 | if startsWith(f, 'oauth_') 41 | A = obj.addrow(A, f{1}, a.(f{1})); 42 | end 43 | end 44 | timestamp = round(java.lang.System.currentTimeMillis / 1000); 45 | A = obj.addrow(A, 'oauth_timestamp', num2str(timestamp)); 46 | A = obj.addrow(A, 'oauth_nonce', dec2hex(round(rand*1e15))); 47 | B = sortrows([obj.req.query; A], 'name'); 48 | base = strjoin(B.name + "=" + B.value, '&'); 49 | base = strjoin({obj.method obj.req.urlencode(obj.req.geturl()) obj.req.urlencode(base)}, '&'); 50 | key = join(string(a.secret), '&'); 51 | sign = obj.HMAC(base, key, a.oauth_signature_method); 52 | A = obj.addrow(A, 'oauth_signature', sign); 53 | if isfield(a, 'realm') 54 | A = obj.addrow(A, 'realm', a.realm); 55 | end 56 | if isfield(a, 'authtype') && strcmpi(a.authtype, 'header') 57 | h = "OAuth " + strjoin(A.name + "=""" + obj.req.urlencode(A.value) + """", ', '); 58 | obj.req.addheader('Authorization', h); 59 | else 60 | obj.req.addquery(A); 61 | end 62 | req = obj.req; 63 | end 64 | 65 | function req = oauth20(obj) 66 | %% OAuth 2.0 67 | if ~isempty(obj.authdata) 68 | obj.req.addquery('access_token', obj.authdata.access_token); 69 | end 70 | req = obj.req; 71 | end 72 | 73 | function res = gettoken(obj, params) 74 | %% Get auth token 75 | if nargin < 2 76 | params = ''; 77 | end 78 | obj.auth(); 79 | if strcmpi(obj.method, 'browser') 80 | res = obj.getfrombrowser(params); 81 | else 82 | res = obj.req.call(obj.method); 83 | if ~isstruct(res) 84 | res = obj.parsequery(res, params); 85 | end 86 | end 87 | end 88 | 89 | function auth_data = gettoken20(obj, type, client_secret) 90 | %% Get access token for Auth 2.0 91 | if strcmpi(type, 'implicit') % Implicit Auth 2.0 92 | obj.req.addquery(obj.authdata); 93 | obj.method = 'browser'; 94 | auth_data = obj.gettoken({'access_token' 'expires_in'}); 95 | elseif strcmpi(type, 'code') % Authorization Code Auth 2.0 96 | reqs = obj.req; 97 | req = reqs(1); %#ok<*PROPLC> 98 | req.addquery(obj.authdata); 99 | obj.req = req; 100 | obj.method = 'browser'; 101 | c = obj.gettoken('code'); 102 | req = reqs(2); 103 | req.addquery(obj.authdata); 104 | req.addquery('client_secret', client_secret); 105 | req.addquery('code', c.code); 106 | obj.req = req; 107 | obj.method = 'GET'; 108 | auth_data = obj.gettoken(); 109 | end 110 | auth_data.date = datetime('now'); 111 | auth_data.expires_in = str2double(string(auth_data.expires_in)); 112 | end 113 | 114 | function [values, url] = getfrombrowser(obj, params) 115 | %% Open URL in browser and get specified params values 116 | url = ''; 117 | isExt = com.mathworks.mlwidgets.html.HTMLPrefs.isSystemBrowserForExternalSites().booleanValue(); 118 | if isExt 119 | com.mathworks.mlwidgets.html.HTMLPrefs.setSystemBrowserForExternalSites(java.lang.Boolean(false)); 120 | end 121 | [~, h, ~] = web(obj.req.getfullurl(), '-new'); 122 | while ~h.isValid 123 | % wait till Web Browser is ready 124 | end 125 | done = false; 126 | while ~done 127 | if ~isempty(h.getActiveBrowser) 128 | url = char(h.getCurrentLocation()); 129 | else 130 | break 131 | end 132 | done = all(contains(url, params + "=")); 133 | end 134 | values = obj.parsequery(url, params); 135 | close(h); 136 | if isExt 137 | com.mathworks.mlwidgets.html.HTMLPrefs.setSystemBrowserForExternalSites(java.lang.Boolean(true)); 138 | end 139 | end 140 | 141 | function ps = parsequery(~, url, params) 142 | %% Get query parameters from URL 143 | if nargin < 3 || isempty(params) 144 | qstr = '[^&#]+'; 145 | else 146 | qstr = sprintf('(%s)([^&#]+)', char(join(params, '|'))); 147 | end 148 | ps = regexp(url, qstr, 'match'); 149 | ps = split(ps', '='); 150 | ps = reshape(ps, [], 2); 151 | ps = cell2struct(ps(:,2), ps(:,1)); 152 | end 153 | 154 | function sign = HMAC(~, str, key, alg) 155 | %% HMAC encryption 156 | import java.net.*; 157 | import javax.crypto.*; 158 | import javax.crypto.spec.*; 159 | import org.apache.commons.codec.binary.* 160 | if contains(alg, 'SHA1') 161 | alg = 'HmacSHA1'; 162 | elseif contains(alg, 'SHA256') 163 | alg = 'HmacSHA256'; 164 | else 165 | error("Unknown HMAC Algorithm: " + alg); 166 | end 167 | keyStr = java.lang.String(key); 168 | key = SecretKeySpec(keyStr.getBytes(), alg); 169 | mac = Mac.getInstance(alg); 170 | mac.init(key); 171 | bytes = java.lang.String(str).getBytes(); 172 | sign = mac.doFinal(bytes); 173 | sign = java.lang.String(Base64.encodeBase64(sign)); 174 | sign = strtrim((sign.toCharArray())'); 175 | end 176 | end 177 | 178 | 179 | methods (Access = private) 180 | 181 | function T = newtable(~) 182 | %% New empty name/value table 183 | T = cell2table(cell(0, 2), 'VariableNames', {'name', 'value'}); 184 | end 185 | 186 | function T = addrow(~, T, name, value) 187 | %% Add row to table 188 | i = strcmpi(T.name, name); 189 | if any(i) 190 | T.value{i} = value; 191 | else 192 | T = [T; {name value}]; 193 | end 194 | end 195 | 196 | end 197 | end -------------------------------------------------------------------------------- /+WEB/+API/Common.m: -------------------------------------------------------------------------------- 1 | classdef (Abstract) Common < handle 2 | % APIs Common Class 3 | 4 | properties 5 | timeout = 15 6 | end 7 | 8 | methods 9 | function obj = Common() 10 | % API Construct an instance of this class 11 | end 12 | 13 | function [params, apiopts] = prepare_params(~, ps, vars) 14 | %% Prepare method parameters 15 | if (nargin > 1) && ~isempty(ps) 16 | reqps = ps(ps(:, 2) == "required", :); 17 | optps = ps(ps(:, 2) == "optional", :); 18 | paropps = ps(ismember(ps(:, 2), {'optional', 'apiOption'}), :); 19 | apips = ps(ps(:, 2) == "apiOption", :); 20 | params = struct(); 21 | apiopts = struct(); 22 | if ~isempty(paropps) 23 | p = inputParser; 24 | for i = 1 : size(paropps, 1) 25 | addParameter(p, paropps{i, 1}, paropps{i, 3}); 26 | end 27 | parse(p, vars{:}); 28 | params = p.Results; 29 | params = rmfield(params, intersect(p.UsingDefaults, optps(:,1))); 30 | end 31 | for i = 1 : size(reqps, 1) 32 | params.(reqps{i, 1}) = (reqps{i, 3}); 33 | end 34 | for i = 1 : size(apips, 1) 35 | fname = apips{i, 1}; 36 | apiopts.(fname) = params.(fname); 37 | params = rmfield(params, fname); 38 | end 39 | else 40 | params = {}; 41 | apiopts = {}; 42 | end 43 | end 44 | 45 | function [items, count] = extract(~, resp, names) 46 | %% Extract items from repsonse 47 | names = string(names); 48 | for i = 1 : length(names) 49 | name = names(i); 50 | if isstruct(resp) && isfield(resp, name) 51 | items = resp.(name); 52 | break; 53 | else 54 | items = resp; 55 | end 56 | end 57 | if isstruct(items) 58 | items = struct2table(items, 'AsArray', 1); 59 | end 60 | if isfield(resp, 'count') 61 | count = resp.count; 62 | else 63 | count = []; 64 | end 65 | end 66 | 67 | end 68 | end 69 | 70 | -------------------------------------------------------------------------------- /+WEB/+API/Doc.m: -------------------------------------------------------------------------------- 1 | function Doc 2 | TE = MATLABWEBAPIExtender; 3 | TE.doc(); -------------------------------------------------------------------------------- /+WEB/+API/Examples.m: -------------------------------------------------------------------------------- 1 | function Examples 2 | TE = MATLABWEBAPIExtender; 3 | TE.examples(); -------------------------------------------------------------------------------- /+WEB/+API/README.md: -------------------------------------------------------------------------------- 1 | [English](#web-api-key-components-description-gb) | [Русский](#Описание-основных-компонентов-web-api-ru) 2 | 3 | # WEB API key components description :gb: 4 | 5 | Key components for any WEB API. 6 | 7 | ## [Common](Common.m) 8 | 9 | Common class that any WEB API inherits. Contains methods required for API building: 10 | - **prepare_params** - preparing of WEB API method calling parameters 11 | - **extract** - extraction of data arrays from response 12 | 13 | ## [Auth](Auth.m) 14 | 15 | Use this class to add to your WEB API OAuth 1.0 ([example](../Flickr.m)) and OAuth 2.0 ([example](../VK.m)) support. 16 | 17 | ## [Req](Req.m) 18 | 19 | Library for HTTP WEB requests. More handy alternative to builtin [webread](https://www.mathworks.com/help/matlab/ref/webread.html) and [webwrite](https://www.mathworks.com/help/matlab/ref/webwrite.html). 20 | 21 | [Example](../../examples/req_example.m) of using. 22 | 23 | #### Main functions: 24 | - `seturl` - set request base URL 25 | - `addurl` - add method to request URL 26 | - `getfullurl` - get full URL with query parameters 27 | - `addquery` - add a query parameter 28 | - `addbody` - add body field 29 | - `addheader` - add header field 30 | - `setopts` - set request options (see [weboptions](https://www.mathworks.com/help/matlab/ref/weboptions.html)) 31 | - `get`, `post`, `put`, `delete`, `patch` - perform request 32 | 33 | #### All functions: 34 | `doc WEB.API.Req` 35 | 36 | 37 | 38 | # Описание основных компонентов WEB API :ru: 39 | 40 | Ключевые компоненты для создания любого WEB API. 41 | 42 | ## [Common](Common.m) 43 | 44 | Общий класс, который наследует каждый WEB API. Содержит методы, необходимые для построения API: 45 | - **prepare_params** - подготовка параметров, которые передаются при вызове конкретонго метода WEB API 46 | - **extract** - извлечение массивов данных из результата запроса 47 | 48 | ## [Auth](Auth.m) 49 | 50 | Этот класс позволяет добавить в WEB API поддержку авторизации OAuth 1.0 ([пример](../Flickr.m)) и OAuth 2.0 ([пример](../VK.m)) 51 | 52 | ## [Req](Req.m) 53 | 54 | Библиотека для создания и выполнения HTTP WEB запросов. Является более удобной альтернативой применению команд [webread](https://www.mathworks.com/help/matlab/ref/webread.html), [webwrite](https://www.mathworks.com/help/matlab/ref/webwrite.html). 55 | 56 | [Пример](../../examples/req_example.m) использования. 57 | 58 | #### Основные функции: 59 | - `seturl` - установить адрес запроса 60 | - `addurl` - добавить к адресу название метода 61 | - `getfullurl` - получить полный адрес с учетом параметров запроса 62 | - `addquery` - добавить параметр запроса 63 | - `addbody` - добавить тело запроса 64 | - `addheader` - добавить заголовок запроса 65 | - `setopts` - задать настройки запроса (см. [weboptions](https://www.mathworks.com/help/matlab/ref/weboptions.html)) 66 | - `get`, `post`, `put`, `delete`, `patch` - выполнить запрос 67 | 68 | #### Все функции: 69 | `doc WEB.API.Req` -------------------------------------------------------------------------------- /+WEB/+API/Req.m: -------------------------------------------------------------------------------- 1 | classdef Req < handle 2 | % Library for easy work with WEB requests 3 | 4 | properties 5 | url % Request URL 6 | query % Request Query Fields 7 | body % Request Body Ffields 8 | opts % Request Options 9 | end 10 | 11 | methods 12 | function obj = Req(url) 13 | %% Create new Req object 14 | if nargin < 1 15 | url = ''; 16 | end 17 | obj.url = url; 18 | obj.clearquery(); 19 | obj.clearbody(); 20 | obj.clearopts(); 21 | end 22 | 23 | function value = getheader(obj, name) 24 | %% Get header fields 25 | value = obj.convert(obj.opts.HeaderFields); 26 | if nargin > 1 27 | value = obj.getbyname(value, name); 28 | end 29 | end 30 | 31 | function header = setheader(obj, header) 32 | %% Set header (i.e. for JSON data) 33 | if istable(header) 34 | header = obj.convert(header); 35 | end 36 | obj.opts.HeaderFields = header; 37 | end 38 | 39 | function header = addheader(obj, name, value) 40 | %% Add header fields 41 | header = obj.convert(obj.opts.HeaderFields); 42 | if isnumeric(value) 43 | value = num2str(value); 44 | end 45 | header = obj.addrow(header, name, value); 46 | obj.opts.HeaderFields = obj.convert(header); 47 | end 48 | 49 | function header = clearheader(obj, header) 50 | %% Clear header fields 51 | opts = weboptions(); 52 | obj.opts.HeaderFields = opts.HeaderFields; 53 | end 54 | 55 | function value = getquery(obj, name) 56 | %% Get query fields 57 | value = obj.query; 58 | if nargin > 1 59 | value = obj.getbyname(value, name); 60 | end 61 | end 62 | 63 | function query = setquery(obj, query) 64 | %% Set query fields 65 | if isempty(query) 66 | obj.clearquery(); 67 | else 68 | if isstruct(query) 69 | obj.clearquery(); 70 | for f = fieldnames(query)' 71 | obj.addquery(f{1}, query.(f{1})); 72 | end 73 | else 74 | obj.query = query; 75 | end 76 | end 77 | query = obj.query; 78 | end 79 | 80 | function query = addquery(obj, name, value) 81 | %% Add query fields 82 | if istable(name) 83 | name = table2cell(name); 84 | elseif isstruct(name) 85 | name = [fieldnames(name) struct2cell(name)]; 86 | end 87 | if iscell(name) 88 | name(:, 2) = obj.urlencode(name(:, 2)); 89 | query = [obj.query; name]; 90 | else 91 | query = obj.addrow(obj.query, obj.urlencode(name), obj.urlencode(value)); 92 | end 93 | obj.query = query; 94 | end 95 | 96 | function query = clearquery(obj) 97 | %% Clear query fields 98 | query = obj.newtable(); 99 | obj.query = query; 100 | end 101 | 102 | function value = getbody(obj, name) 103 | %% Get body 104 | value = obj.body; 105 | if nargin > 1 106 | value = obj.getbyname(value, name); 107 | end 108 | end 109 | 110 | function body = setbody(obj, body, asTable) 111 | %% Set body 112 | if nargin < 3 113 | asTable = false; 114 | end 115 | if isempty(body) 116 | obj.clearbody(); 117 | else 118 | if isstruct(body) && asTable 119 | obj.clearbody(); 120 | for f = fieldnames(body)' 121 | obj.addbody(f{1}, body.(f{1})); 122 | end 123 | else 124 | obj.body = body; 125 | end 126 | end 127 | body = obj.body; 128 | end 129 | 130 | function body = addbody(obj, name, value) 131 | %% Add body fields 132 | if istable(name) || iscell(name) 133 | body = [obj.body; name]; 134 | else 135 | body = obj.addrow(obj.body, name, value); 136 | end 137 | obj.body = body; 138 | end 139 | 140 | function body = clearbody(obj) 141 | %% Clear body 142 | body = obj.newtable(); 143 | obj.body = body; 144 | end 145 | 146 | function url = geturl(obj) 147 | %% Get Request URL 148 | url = char(obj.url); 149 | end 150 | 151 | function seturl(obj, url) 152 | %% Set Request URL 153 | obj.url = string(url); 154 | end 155 | 156 | function addurl(obj, add) 157 | %% Add method to URL 158 | if ~isempty(obj.url) 159 | obj.url = strip(obj.url, "/") + "/" + strip(add, "/"); 160 | end 161 | end 162 | 163 | function fullurl = getfullurl(obj) 164 | %% Get full Request url (i.e. for web browser or 'webread') 165 | fullurl = char(obj.url); 166 | if endsWith(fullurl, '/') 167 | fullurl(end) = ''; 168 | end 169 | if ~isempty(obj.query) 170 | fullurl = fullurl + "?" + strjoin(obj.query.name + "=" + obj.query.value, '&'); 171 | end 172 | fullurl = char(fullurl); 173 | end 174 | 175 | function opts = getopts(obj) 176 | %% Get Request options 177 | opts = obj.opts; 178 | end 179 | 180 | function opts = setopts(obj, name, value) 181 | %% Set particular web option (see 'weboptions' function) 182 | if nargin == 2 183 | if class(name) == "weboptions" % set whole set of options 184 | obj.opts = name; 185 | else 186 | error('Not enough input arguments'); 187 | end 188 | else 189 | obj.opts.(name) = value; 190 | end 191 | opts = obj.opts; 192 | end 193 | 194 | function opts = clearopts(obj) 195 | %% Reset Request options 196 | opts = weboptions; 197 | obj.opts = opts; 198 | end 199 | 200 | function [resp, err] = call(obj, method) 201 | %% Perfom request 202 | switch lower(method) 203 | case 'get' 204 | [resp, err] = obj.get(); 205 | case 'post' 206 | [resp, err] = obj.post(); 207 | case 'put' 208 | [resp, err] = obj.put(); 209 | case 'delete' 210 | [resp, err] = obj.delete(); 211 | case 'patch' 212 | [resp, err] = obj.patch(); 213 | otherwise 214 | error('Unknown request method') 215 | end 216 | end 217 | 218 | function [resp, err] = get(obj) 219 | %% Perform GET request 220 | err = false; 221 | resp = []; 222 | try 223 | resp = webread(obj.getfullurl(), obj.opts); 224 | catch e 225 | err = e.message; 226 | end 227 | end 228 | 229 | function [resp, err] = post(obj) 230 | %% Perform POST request 231 | err = false; 232 | resp = []; 233 | try 234 | if obj.opts.MediaType == "application/x-www-form-urlencoded" 235 | form = table2cell(obj.body)'; 236 | fp = matlab.net.http.io.MultipartFormProvider(form{:}); 237 | req = matlab.net.http.RequestMessage('post', [], fp); 238 | resp = req.send(obj.getfullurl()); 239 | else 240 | resp = webwrite(obj.getfullurl(), obj.getstruct(obj.body), obj.opts); 241 | end 242 | catch e 243 | err = e.message; 244 | end 245 | end 246 | 247 | function [resp, err] = put(obj) 248 | %% Perform PUT request 249 | err = false; 250 | resp = []; 251 | obj.opts.RequestMethod = 'put'; 252 | try 253 | resp = webwrite(obj.getfullurl(), obj.getstruct(obj.body), obj.opts); 254 | catch e 255 | err = e.message; 256 | end 257 | end 258 | 259 | function [resp, err] = delete(obj) 260 | %% Perform DELETE request 261 | err = false; 262 | resp = []; 263 | obj.opts.RequestMethod = 'delete'; 264 | try 265 | resp = webwrite(obj.getfullurl(), obj.getstruct(obj.body), obj.opts); 266 | catch e 267 | err = e.message; 268 | end 269 | end 270 | 271 | function [resp, err] = patch(obj) 272 | %% Perform PATCH request 273 | err = false; 274 | resp = []; 275 | obj.opts.RequestMethod = 'patch'; 276 | try 277 | resp = webwrite(obj.getfullurl, obj.getstruct(obj.body), obj.opts); 278 | catch e 279 | err = e.message; 280 | end 281 | end 282 | 283 | function T = newtable(~) 284 | %% New empty name/value table 285 | T = cell2table(cell(0, 2), 'VariableNames', {'name', 'value'}); 286 | end 287 | 288 | function T = addrow(~, T, name, value) 289 | %% Add row to table 290 | i = strcmpi(T.name, name); 291 | if any(i) 292 | T.value{i} = value; 293 | else 294 | T = [T; {name value}]; 295 | end 296 | end 297 | 298 | function value = getbyname(~, T, name) 299 | %% Get value by name 300 | value = T.value(strcmp(T.name, name)); 301 | if length(value) == 1 302 | value = char(value); 303 | end 304 | end 305 | 306 | function h = convert(obj, h) 307 | %% Convert header between formats 308 | if istable(h) 309 | h = h{:, :}; 310 | else 311 | T = obj.newtable(); 312 | if iscell(h) || isstring(h) 313 | h = cellstr(h); 314 | for i = 1 : size(h, 1) 315 | T = [T; h(i, :)]; 316 | end 317 | end 318 | h = T; 319 | end 320 | end 321 | 322 | function data = getstruct(~, data) 323 | %% convert data to struct 324 | if ~isempty(data) 325 | if istable(data) 326 | for i = 1 : height(data) 327 | s.(char(data{i, 1})) = data{i, 2}; 328 | end 329 | data = s; 330 | end 331 | else 332 | data = struct(); 333 | end 334 | end 335 | 336 | function txt = decode(~, txt) 337 | %% Decode text 338 | txt = unicode2native(txt, 'utf-8'); 339 | txt = native2unicode(txt, 'windows-1251'); 340 | end 341 | 342 | function txt = urlencode(~, txt) 343 | %% Fixed urlencode 344 | if isnumeric(txt) && ~isscalar(txt) 345 | txt = join(string(txt(:)), ','); 346 | else 347 | txt = string(txt); 348 | end 349 | txt = txt(:); 350 | for i = 1 : length(txt) 351 | txt(i) = char(java.net.URLEncoder.encode(txt(i), 'UTF-8')); 352 | txt(i) = strrep(txt(i), '+', '%20'); 353 | end 354 | if isscalar(txt) 355 | txt = char(txt); 356 | else 357 | txt = cellstr(txt); 358 | end 359 | end 360 | 361 | end 362 | end -------------------------------------------------------------------------------- /+WEB/+API/Update.m: -------------------------------------------------------------------------------- 1 | function Update(force) 2 | % Update MATLAB WEB API from GitHub to latest release 3 | if nargin < 1 4 | force = 0; 5 | end 6 | TU = MATLABWEBAPIUpdater; 7 | if ~force 8 | TU.update(); 9 | else 10 | TU.installweb(); 11 | end 12 | -------------------------------------------------------------------------------- /+WEB/+API/Ver.m: -------------------------------------------------------------------------------- 1 | function Ver 2 | % Check local and latest version 3 | TU = MATLABWEBAPIUpdater; 4 | TU.ver(); -------------------------------------------------------------------------------- /+WEB/+Utils/Plotter.m: -------------------------------------------------------------------------------- 1 | classdef Plotter 2 | % Helper functions for plotting 3 | 4 | methods 5 | function obj = Plotter() 6 | % Plotter Construct an instance of this class 7 | end 8 | 9 | function g = geoBubble(~, gc, varargin) 10 | %% Plot geobubble map 11 | p = inputParser; 12 | p.addRequired('gc'); 13 | p.addParameter('Size', []); 14 | p.addParameter('Color', []); 15 | p.addParameter('Title', ''); 16 | p.parse(gc, varargin{:}); 17 | size = p.Results.Size; 18 | color = p.Results.Color; 19 | if ~iscategorical(color) 20 | color = categorical(string(color)); 21 | end 22 | figure; 23 | g = geobubble(gc(:, 1), gc(:, 2), size, color); 24 | if ~isempty(p.Results.Title) 25 | g.Title = p.Results.Title; 26 | end 27 | end 28 | 29 | function g = geoScatter(~, gc, varargin) 30 | %% Plot geoscatter map 31 | p = inputParser; 32 | p.addRequired('gc'); 33 | p.addParameter('Size', []); 34 | p.addParameter('Color', [0 0 1]); 35 | p.addParameter('Tips', ''); 36 | p.addParameter('Title', ''); 37 | p.parse(gc, varargin{:}); 38 | size = p.Results.Size; 39 | color = p.Results.Color; 40 | figure; 41 | g = geoscatter(gc(:, 1), gc(:, 2), size * 2000, color, '.'); 42 | if ~isempty(p.Results.Title) 43 | title(p.Results.Title); 44 | end 45 | if ~isempty(p.Results.Tips) 46 | dcm = datacursormode(gcf); 47 | dcm.UpdateFcn = @(~, e) pointtips([], e, p.Results.Tips); 48 | end 49 | function txt = pointtips(~, event_obj, tips) 50 | % Customizes text of data tips 51 | i = event_obj.DataIndex; 52 | txt = tips(i, :)'; 53 | end 54 | end 55 | 56 | function ax = image(~, im, varargin) 57 | %% Plot image 58 | p = inputParser; 59 | p.addRequired('im'); 60 | p.addParameter('title', ''); 61 | p.parse(im, varargin{:}); 62 | figure; 63 | image(im); 64 | ax = gca; 65 | ax.Visible = 'off'; 66 | if ~isempty(p.Results.title) 67 | ax.Title = p.Results.title; 68 | end 69 | end 70 | 71 | end 72 | end -------------------------------------------------------------------------------- /+WEB/+Utils/README.md: -------------------------------------------------------------------------------- 1 | [English](#additional-web-api-tools-description-gb) | [Русский](#Описание-дополнительных-библиотек-web-api-ru) 2 | 3 | # Additional WEB API tools description :gb: 4 | 5 | Additional libraries for WEB APIs. 6 | 7 | ## [Plotter](Plotter.m) 8 | 9 | Tools for visualization and plotting: 10 | - **map** - [geobubble](https://www.mathworks.com/help/matlab/ref/geobubble.html) map 11 | - **image** - image drawing 12 | 13 | ## [Storage](Storage.m) 14 | 15 | Work with data storage. Data is stored in `data` variable in *.mat-file. 16 | 17 | ## [Tables](Tables.m) 18 | 19 | Additional functions for more convinient work with [tables](https://www.mathworks.com/help/search.html). 20 | 21 | 22 | 23 | # Описание дополнительных библиотек WEB API :ru: 24 | 25 | Дополнительные инструменты для WEB API. 26 | 27 | ## [Plotter](Plotter.m) 28 | 29 | Инструменты визуализации и построения графиков: 30 | - **map** - карта [geobubble](https://www.mathworks.com/help/matlab/ref/geobubble.html) 31 | - **image** - отрисовка изображения 32 | 33 | ## [Storage](Storage.m) 34 | 35 | Работа с хранилищем данных. Данные хранятся в переменной `data` в *.mat-файле. 36 | 37 | ## [Tables](Tables.m) 38 | 39 | Дополнительные функции для более удобной работы с [таблицами](https://www.mathworks.com/help/matlab/tables.html). 40 | -------------------------------------------------------------------------------- /+WEB/+Utils/Storage.m: -------------------------------------------------------------------------------- 1 | classdef Storage < handle 2 | % Data Storage Class 3 | 4 | properties 5 | data % Data 6 | path = '' % Data Path 7 | file = 'data.mat' % Data File Name 8 | end 9 | 10 | methods 11 | function obj = Storage(file, path) 12 | %% Storage Construct an instance of this class 13 | obj.file = file; 14 | if nargin > 1 15 | obj.path = path; 16 | end 17 | obj.load(); 18 | end 19 | 20 | function set.path(obj, path) 21 | %% Set data path 22 | obj.path = path; 23 | obj.load(); 24 | end 25 | 26 | function path = get.path(obj) 27 | %% Get path 28 | path = obj.path; 29 | end 30 | 31 | function file = get.file(obj) 32 | %% Get file name 33 | file = obj.file; 34 | end 35 | 36 | function set.file(obj, file) 37 | %% Set file name 38 | obj.file = file; 39 | obj.load(); 40 | end 41 | 42 | function data = get.data(obj) 43 | %% Get data 44 | data = obj.data; 45 | end 46 | 47 | function set.data(obj, data) 48 | %% Set data 49 | obj.data = data; 50 | end 51 | 52 | function fp = fullpath(obj) 53 | %% Full path to data 54 | fp = fullfile(obj.path, obj.file); 55 | end 56 | 57 | function data = load(obj) 58 | %% Load data from file 59 | if isfile(obj.fullpath()) 60 | load(obj.fullpath(), 'data'); 61 | else 62 | data = []; 63 | end 64 | obj.data = data; 65 | end 66 | 67 | function data = save(obj, data) 68 | %% Save data to file 69 | if nargin > 1 70 | obj.data = data; 71 | else 72 | data = obj.data; 73 | end 74 | save(obj.fullpath(), 'data'); 75 | end 76 | 77 | function clear(obj) 78 | %% Delete data and file 79 | obj.data = []; 80 | if isfile(obj.fullpath()) 81 | delete(obj.fullpath()); 82 | end 83 | end 84 | 85 | function val = get_cm(obj, key, cm) 86 | %% Get from containers map 87 | if nargin < 3 88 | cm = obj.data; 89 | end 90 | val = []; 91 | if ~isempty(cm) && cm.isKey(key) 92 | val = cm(key); 93 | end 94 | end 95 | 96 | function cm = set_cm(obj, key, val, cm) 97 | %% Set containers map value 98 | if nargin < 4 99 | cm = obj.data; 100 | end 101 | if isempty(cm) 102 | cm = containers.Map(); 103 | end 104 | cm(key) = val; 105 | if nargin < 4 106 | obj.data = cm; 107 | end 108 | end 109 | 110 | function imsave(obj, fname, im) 111 | %% Save image 112 | if nargin < 2 113 | im = obj.data; 114 | end 115 | imwrite(im, fullfile(obj.path, char(fname))); 116 | end 117 | 118 | end 119 | end 120 | 121 | -------------------------------------------------------------------------------- /+WEB/+Utils/Tables.m: -------------------------------------------------------------------------------- 1 | classdef Tables 2 | % Additional functions for tables 3 | 4 | methods 5 | function obj = Tables() 6 | % Tables Construct an instance of this class 7 | end 8 | 9 | function ss = concat(obj, ss, preserveFormat) 10 | %% Fix VK Response 11 | ist = false; 12 | if iscell(ss) 13 | ss = ss(:); 14 | emp = cellfun('isempty', ss); 15 | if all(emp) 16 | ss = {}; 17 | elseif any(emp) 18 | ss = ss(~emp); 19 | end 20 | if ~isempty(ss) 21 | ist = cellfun(@istable, ss); 22 | ss(ist) = cellfun(@(x) table2struct(x), ss(ist), 'un', 0); 23 | fs = cellfun(@(x) fieldnames(x), ss, 'un', 0); 24 | fs = reshape(vertcat(fs{:}), [], 1); 25 | fs = unique(fs); 26 | ss = obj.fill_empty(ss, fs); 27 | ss = vertcat(ss{:}); 28 | end 29 | if any(ist) || nargin < 3 || ~preserveFormat 30 | if isstruct(ss) 31 | ss = struct2table(ss, 'AsArray', true); 32 | end 33 | end 34 | end 35 | end 36 | 37 | function ss = fill_empty(obj, ss, fields, emptvals) 38 | %% Fill absent values with empty 39 | if nargin < 4 40 | emptvals = {-1, ''}; 41 | end 42 | ist = istable(ss); 43 | if ist 44 | ss = {table2struct(ss)}; 45 | end 46 | iscm = isa(fields, 'containers.Map'); 47 | if iscm 48 | fs = keys(fields); 49 | else 50 | fs = fields; 51 | end 52 | for f = fs(:)' 53 | fex = cellfun(@(x, f) isfield(x, f), ss, repmat(f, length(ss), 1)); 54 | if iscm 55 | if fields(f{1}) == "double" 56 | e = emptvals{1}; 57 | else 58 | e = emptvals{2}; 59 | end 60 | else 61 | tc = cellfun(@(x, f) class(getfield(x, f)), ss(fex), repmat(f, length(ss(fex)), 1), 'un', 0); 62 | if length(unique(tc)) == 1 && tc{1} == "double" 63 | e = emptvals{1}; 64 | else 65 | e = emptvals{2}; 66 | end 67 | end 68 | ss(~fex) = obj.addfield(ss(~fex), f{1}, e); 69 | end 70 | if ist 71 | ss = struct2table(ss{1}, 'AsArray', true); 72 | end 73 | end 74 | 75 | function a = cell2double(~, c, fs) 76 | %% Convert cell array of doubles to numeric array 77 | ist = istable(c); 78 | if ist 79 | t = c; 80 | c = t{:, fs}; 81 | end 82 | c = cellfun(@double, c, 'un', 0); 83 | ise = cellfun('isempty', c); 84 | c(ise) = {NaN}; 85 | a = cell2mat(c); 86 | if ist 87 | fs = string(fs); 88 | for i = 1 : length(fs) 89 | t.(fs(i)) = a(:, i); 90 | end 91 | a = t; 92 | end 93 | end 94 | 95 | function T = cm2table(obj, cm) 96 | %% Convert containers.Map to table 97 | k = keys(cm)'; 98 | v = values(cm)'; 99 | if istable(v{1}) || isstruct(v{1}) 100 | v = obj.concat(v); 101 | else 102 | v = reshape([v{:}], 2, [])'; 103 | end 104 | T = table(k, v, 'VariableNames', {'Keys' 'Values'}); 105 | end 106 | 107 | function t = fillmissingfrom(~, t, var1, var2) 108 | %% Fill empty values in var1 with values from var2 109 | ise = ismissing(t{:, var1}); 110 | t{ise, var1} = t{ise, var2}; 111 | end 112 | 113 | function ss = addfield(~, ss, f, val) 114 | %% Add field to struct cell 115 | isc = iscell(ss); 116 | ist = istable(ss); 117 | if ist, ss = table2struct(ss); end 118 | if ~isc, ss = {ss}; end 119 | if ~iscell(val), val = {val}; end 120 | addfwrap = @(x) addfield_helper(x, f, val); 121 | ss = cellfun(@(x) addfwrap(x), ss, 'un', 0); 122 | function x = addfield_helper(x, f, e) 123 | em = repmat(e, length(x), 1); 124 | if iscell(em) 125 | [x.(f)] = em{:}; 126 | else 127 | [x.(f)] = em; 128 | end 129 | end 130 | if ~isc, ss = ss{1}; end 131 | if ist, ss = struct2table(ss, 'AsArray', true); end 132 | end 133 | 134 | function s = extract(~, s, f, subf, newf) 135 | %% Extract subfield from field 136 | if nargin < 5 137 | newf = f; 138 | end 139 | ist = istable(s); 140 | if ist 141 | s = table2struct(s); 142 | end 143 | if isfield(s, f) 144 | vals = {s.(f)}'; 145 | isVal = ~cellfun('isempty', vals); 146 | hasField = false(size(isVal)); 147 | hasField(isVal) = cellfun(@(c) isfield(c, subf), vals(isVal)); 148 | vals(hasField) = cellfun(@(c) c.(subf), vals(hasField), 'un', 0); 149 | c = cellfun(@class, vals(hasField), 'un', 0); 150 | c = unique(c); 151 | if length(c) == 1 152 | if c == "double" 153 | emp = {-1}; 154 | elseif c == "char" 155 | emp = {''}; 156 | end 157 | vals(~hasField) = emp; 158 | end 159 | [s.(newf)] = vals{:}; 160 | end 161 | if ist, s = struct2table(s, 'AsArray', true); end 162 | end 163 | 164 | function t = expand(obj, t, f, preserve, append_names) 165 | %% Expand table's structure variable to new variables 166 | if nargin < 4 167 | preserve = false; 168 | end 169 | if nargin < 5 170 | append_names = true; 171 | end 172 | newt = obj.concat(t{:, f}); 173 | if isstruct(newt) 174 | newt = struct2table(newt); 175 | end 176 | vnames = newt.Properties.VariableNames; 177 | if append_names 178 | vnames = f + "_" + vnames; 179 | end 180 | newt.Properties.VariableNames = vnames; 181 | t = [t newt]; 182 | if ~preserve 183 | t(:, f) = []; 184 | end 185 | end 186 | 187 | function t = rmvars(~, t, vars) 188 | %% Remove vars from table 189 | if ~iscell(vars) 190 | vars = {vars}; 191 | end 192 | isVar = ismember(vars, t.Properties.VariableNames); 193 | rmVar = vars(isVar); 194 | if ~isempty(rmVar) 195 | t = removevars(t, rmVar); 196 | end 197 | end 198 | 199 | function t = selectvars(~, t, vnames) 200 | %% Order variables 201 | if isa(vnames, 'containers.Map') 202 | vnames = keys(vnames); 203 | end 204 | t = t(:, vnames); 205 | end 206 | 207 | function t = count(~, vec, vname) 208 | %% Count occurences in vector 209 | vec = categorical(vec); 210 | t = table(categories(vec), countcats(vec), 'VariableNames', {vname, 'count'}); 211 | end 212 | 213 | function t = unique(~, t, vname) 214 | %% Unique rows in table by vname 215 | [~, ia] = unique(t{:, vname}); 216 | t = t(ia, :); 217 | end 218 | 219 | function yes = isvar(~, t, vname) 220 | %% Check table has variable 221 | yes = ismember(string(vname), t.Properties.VariableNames); 222 | end 223 | end 224 | end -------------------------------------------------------------------------------- /+WEB/BingMaps.m: -------------------------------------------------------------------------------- 1 | classdef BingMaps < WEB.API.Common 2 | % Bing Maps 3 | % https://msdn.microsoft.com/en-us/library/ff701713.aspx 4 | 5 | properties 6 | URL = "http://dev.virtualearth.net/REST/v1/" % Base URL 7 | key % API Key 8 | storage % Data Storage 9 | end 10 | 11 | methods 12 | function obj = BingMaps(api_key) 13 | %% BingMaps Construct an instance of this class 14 | obj.key = api_key; 15 | obj.storage = WEB.Utils.Storage('bingmaps_gc_data.mat'); 16 | end 17 | 18 | function set_data_path(obj, path) 19 | %% Set auth data path 20 | obj.storage.path = path; 21 | end 22 | 23 | function [res, apiopts] = call_api(obj, method, params, vars) 24 | %% Get via API 25 | [params, apiopts] = obj.prepare_params(params, vars); 26 | req = WEB.API.Req(obj.URL); 27 | req.addurl(method); 28 | req.setquery(params); 29 | req.addquery('key', obj.key); 30 | req.setopts('Timeout', obj.timeout); 31 | res = get(req); 32 | if isempty(res) 33 | error('API Error: empty result') 34 | elseif isfield(res, 'resourceSets') 35 | res = res.resourceSets.resources; 36 | if ~isempty(res) 37 | res = struct2table(res, 'AsArray', 1); 38 | res.x__type = []; 39 | end 40 | end 41 | end 42 | 43 | function [res, data] = location_findByQuery(obj, query, varargin) 44 | %% Find location by query 45 | method = 'Locations'; 46 | params = {'query', 'required', query 47 | 'maxResults', 'optional', 5 48 | 'incl', 'optional', '' 49 | 'inclnb', 'optional', 0 50 | 'useStorage', 'apiOption', 0 51 | 'storeAll', 'apiOption', 0 52 | 'plot', 'apiOption', 0}; 53 | [~, apiopts] = obj.prepare_params(params, varargin); 54 | data = []; 55 | res = []; 56 | if apiopts.useStorage 57 | data = obj.storage.get_cm(query); 58 | end 59 | if isempty(data) 60 | res = obj.call_api(method, params, varargin); 61 | if apiopts.storeAll 62 | data = res; 63 | else 64 | data = obj.get_geocode(res); 65 | end 66 | if apiopts.useStorage 67 | obj.storage.set_cm(query, data); 68 | obj.storage.save(); 69 | end 70 | end 71 | if apiopts.plot 72 | obj.plot_geo(res, query); 73 | end 74 | end 75 | 76 | function res = location_findByPoint(obj, point, varargin) 77 | %% Find location by point 78 | req = WEB.API.Req(); 79 | method = "Locations/" + req.urlencode(point); 80 | params = {'includeEntityTypes', 'optional', '' 81 | 'incl', 'optional', '' 82 | 'inclnb', 'optional', 0}; 83 | res = obj.call_api(method, params, varargin); 84 | end 85 | 86 | function [res, geocode] = location_findByAddress(obj, varargin) 87 | %% Find location by address 88 | method = 'Locations'; 89 | params = {'adminDistrict', 'optional', '' 90 | 'locality', 'optional', '' 91 | 'postalCode', 'optional', [] 92 | 'addressLine', 'optional', '' 93 | 'countryRegion', 'optional', '' 94 | 'includeNeighborhood', 'optional', 0 95 | 'include', 'optional', '' 96 | 'maxResults', 'optional', 10 97 | 'plot', 'apiOption', 0}; 98 | [res, apiopts] = obj.call_api(method, params, varargin); 99 | geocode = obj.get_geocode(res); 100 | if apiopts.plot 101 | obj.plot_geo(res); 102 | end 103 | end 104 | 105 | function res = location_recognition(obj, point, varargin) 106 | %% Recognise location 107 | req = WEB.API.Req(); 108 | method = "LocationRecog/" + req.urlencode(point); 109 | params = {'radius', 'optional', 0.25 110 | 'top', 'optional', 10 111 | 'distanceUnit', 'optional', 'km' 112 | 'verboseplacenames', 'optional', false 113 | 'includeEntityTypes', 'optional', 'businessAndPOI'}; 114 | res = obj.call_api(method, params, varargin); 115 | end 116 | 117 | function res = imagery_staticMap(obj, imagerySet, query, varargin) 118 | %% Get map image 119 | method = "Imagery/Map/" + imagerySet + "/"; 120 | formats = struct('road', 'jpeg', 'aerial', 'jpeg', 'aerialwithlabels', 'jpeg',... 121 | 'collinsbart', 'png', 'ordnancesurvey', 'png'); 122 | params = {'mapSize', 'optional', [350 350] 123 | 'mapLayer', 'optional', 'TrafficFlow' 124 | 'mapMetadata', 'optional', 0 125 | 'dpi', 'optional', '' 126 | 'zoomLevel', 'optional', 0 127 | 'show', 'apiOption', 0 128 | 'save', 'apiOption', 0 129 | 'name', 'apiOption', 'map'}; 130 | ps = obj.prepare_params(params, varargin); 131 | if isnumeric(query) 132 | req = WEB.API.Req(); 133 | point = req.urlencode(query); 134 | method = method + point + "/" + ps.zoomLevel; 135 | else 136 | method = method + query; 137 | end 138 | [res, apiopts] = obj.call_api(method, params, varargin); 139 | if apiopts.show 140 | P = WEB.Utils.Plotter(); 141 | P.image(res); 142 | end 143 | if apiopts.save 144 | fmt = "." + formats.(lower(imagerySet)); 145 | obj.storage.imsave(apiopts.name + fmt, res); 146 | end 147 | end 148 | 149 | function geocode = get_geocode(~, res) 150 | %% Extract geocode from response 151 | if ~isempty(res) && ismember('point', res.Properties.VariableNames) 152 | geocode = [res.point.coordinates]'; 153 | else 154 | geocode = []; 155 | end 156 | end 157 | 158 | function g = plot_geo(obj, res, title) 159 | %% Plot geocodes on map 160 | gc = obj.get_geocode(res); 161 | conf = replace(res.confidence, {'Low','Medium','High'}, {'1','2','3'}); 162 | PU = WEB.Utils.Plotter; 163 | g = PU.geoBubble(gc, 'Size', str2double(conf), 'Color', res.name); 164 | g.SizeLegendTitle = 'Confidence'; 165 | g.ColorLegendTitle = 'Name'; 166 | if nargin > 2 167 | g.Title = title; 168 | end 169 | end 170 | 171 | end 172 | end 173 | 174 | -------------------------------------------------------------------------------- /+WEB/DataGov.m: -------------------------------------------------------------------------------- 1 | classdef DataGov < WEB.API.Common 2 | % DataGov.ru API 3 | % https://data.gov.ru/pravila-i-rekomendacii 4 | 5 | properties 6 | URL = 'http://data.gov.ru/api/' % Base URL 7 | access_token % Access Token 8 | end 9 | 10 | methods 11 | function obj = DataGov(access_token) 12 | %% VK Construct an instance of this class 13 | obj.access_token = access_token; 14 | end 15 | 16 | function res = call_api(obj, method, params, vars) 17 | %% Get via VK API 18 | if nargin < 3 19 | params = {}; 20 | vars = {}; 21 | end 22 | [params, apiopts] = obj.prepare_params(params, vars); 23 | req = WEB.API.Req(obj.URL); 24 | req.addurl(method); 25 | req.setquery(params); 26 | req.addquery('access_token', obj.access_token); 27 | req.setopts('ContentType', 'json'); 28 | req.setopts('Timeout', obj.timeout); 29 | res = get(req); 30 | end 31 | 32 | function res = main(obj) 33 | %% Get self info 34 | method = ''; 35 | res = obj.call_api(method); 36 | end 37 | 38 | function res = datasets(obj, varargin) 39 | %% Get datasets 40 | method = 'dataset'; 41 | params = {'organization', 'optional', '' 42 | 'topic', 'optional', ''}; 43 | res = obj.call_api(method, params, varargin); 44 | res = struct2table(res); 45 | end 46 | 47 | function [res, vers] = dataset(obj, id) 48 | %% Get dataset 49 | method = "dataset/" + id; 50 | res = obj.call_api(method); 51 | res.created = datetime(res.created); 52 | res.modified = datetime(res.modified); 53 | if nargout > 1 54 | vers = obj.call_api(method + "/version"); 55 | vers = cellstr(vertcat(vers.created)); 56 | vers = unique(vers); 57 | end 58 | end 59 | 60 | function [res, str, cont] = version(obj, id, ver) 61 | %% Get dataset version info 62 | method = "dataset/" + id + "/version/" + ver; 63 | res = obj.call_api(method); 64 | if length(res) > 1 65 | res = struct2table(res, 'AsArray', 1); 66 | res = unique(res); 67 | res = table2struct(res); 68 | end 69 | if nargout > 1 70 | str = obj.call_api(method + "/structure"); 71 | end 72 | if nargout > 2 73 | cont = obj.call_api(method + "/content"); 74 | if ~isempty(cont) 75 | cont = struct2table(cont, 'AsArray', 1); 76 | end 77 | end 78 | end 79 | 80 | function res = organizations(obj) 81 | %% Get organizations 82 | method = 'organization'; 83 | res = obj.call_api(method); 84 | res = struct2table(res); 85 | res.title = strtrim(res.title); 86 | res = sortrows(res, 'title'); 87 | end 88 | 89 | function [info, data] = organization(obj, id) 90 | %% Get organization datasets 91 | method = "organization/" + id; 92 | info = obj.call_api(method); 93 | if nargout > 1 94 | data = obj.call_api(method + "/dataset"); 95 | data = struct2table(data, 'AsArray', 1); 96 | end 97 | end 98 | 99 | function res = topics(obj) 100 | %% Get datasets topics 101 | method = 'topic'; 102 | res = obj.call_api(method); 103 | res = sort({res.name}'); 104 | end 105 | 106 | function [info, data] = topic(obj, title) 107 | %% Get topic datasets 108 | method = "topic/" + title; 109 | info = obj.call_api(method); 110 | if nargout > 1 111 | data = obj.call_api(method + "/dataset"); 112 | data = struct2table(data, 'AsArray', 1); 113 | end 114 | end 115 | 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /+WEB/Flickr.m: -------------------------------------------------------------------------------- 1 | classdef Flickr < WEB.API.Common 2 | %FLICKR Summary of this class goes here 3 | % Detailed explanation goes here 4 | 5 | properties 6 | URL = "https://www.flickr.com/services/rest" % Base URL 7 | appKey % Application Key 8 | appSecret % Application Secret 9 | storage % Authentication Data 10 | end 11 | 12 | methods 13 | function obj = Flickr(appKey, appSecret) 14 | %% FLICKR Construct an instance of this class 15 | obj.storage = WEB.Utils.Storage('flickr_auth_data.mat'); 16 | obj.appKey = appKey; 17 | obj.appSecret = appSecret; 18 | end 19 | 20 | function set_data_path(obj, path) 21 | %% Set auth data path 22 | obj.storage.path = path; 23 | end 24 | 25 | function auth_data = login(obj, opt) 26 | %% Login to Flickr 27 | if (nargin > 1) && opt == "force" 28 | auth_data = []; 29 | else 30 | auth_data = obj.storage.load(); 31 | end 32 | if isempty(auth_data) 33 | auth_data = obj.getAccessToken(); 34 | obj.storage.save(auth_data); 35 | end 36 | if isempty(auth_data) 37 | error('Flickr API Error: Unsucessfull Authorization'); 38 | end 39 | end 40 | 41 | function logout(obj) 42 | %% Delete auth data 43 | obj.storage.clear(); 44 | end 45 | 46 | function a = getAuth(obj) 47 | %% Set Auth paramaters 48 | ad = obj.storage.data; 49 | tokenSecret = ''; 50 | if ~isempty(ad) 51 | if all(isfield(ad, {'oauth_token', 'oauth_token_secret'})) 52 | a.oauth_token = ad.oauth_token; 53 | tokenSecret = ad.oauth_token_secret; 54 | end 55 | end 56 | a.secret = {obj.appSecret tokenSecret}; 57 | a.oauth_version = '1.0'; 58 | a.oauth_consumer_key = obj.appKey; 59 | a.oauth_signature_method = 'HMAC-SHA1'; 60 | end 61 | 62 | function res = getAccessToken(obj) 63 | %% Get access token 64 | % Get request token 65 | URL = 'https://www.flickr.com/services/oauth/'; %#ok<*PROP> 66 | req = WEB.API.Req(URL); 67 | req.addurl('request_token'); 68 | a = obj.getAuth(); 69 | a.oauth_callback = 'localhost'; 70 | A = WEB.API.Auth(req, a, 'GET'); 71 | rt = A.gettoken(); 72 | a.oauth_token = rt.oauth_token; 73 | % Get auth verifier 74 | req = WEB.API.Req(URL); 75 | req.addurl('authorize'); 76 | req.addquery('oauth_token', rt.oauth_token); 77 | A = WEB.API.Auth(req, [], 'browser'); 78 | res = A.gettoken('oauth_verifier'); 79 | a.oauth_verifier = res.oauth_verifier; 80 | % Get access token 81 | req = WEB.API.Req(URL); 82 | req.addurl('access_token'); 83 | a.secret = {obj.appSecret rt.oauth_token_secret}; 84 | A = WEB.API.Auth(req, a, 'GET'); 85 | res = A.gettoken(); 86 | end 87 | 88 | function res = call_api(obj, method, params, vars) 89 | %% Call Flickr API 90 | if nargin < 3 91 | params = {}; 92 | vars = {}; 93 | end 94 | [params, apiopts] = obj.prepare_params(params, vars); 95 | req = WEB.API.Req(obj.URL); 96 | if ~isempty(params) 97 | req.setquery(params); 98 | end 99 | req.addquery('format', 'json'); 100 | req.addquery('nojsoncallback', 1); 101 | req.addquery('api_key', obj.appKey); 102 | req.addquery('method', method); 103 | A = WEB.API.Auth(req, obj.getAuth(), 'GET'); 104 | req = A.oauth10(); 105 | res = get(req); 106 | obj.check_api_error(res); 107 | end 108 | 109 | function check_api_error(~, resp) 110 | %% Check API Call Error 111 | if isfield(resp, 'stat') && resp.stat == "fail" 112 | error(['API error: ' resp.message]); 113 | end 114 | end 115 | 116 | function res = test_login(obj) 117 | %% Test successful login 118 | method = 'flickr.test.login'; 119 | res = obj.call_api(method); 120 | end 121 | 122 | function res = test_echo(obj) 123 | %% Test echo 124 | method = 'flickr.test.echo'; 125 | res = obj.call_api(method); 126 | end 127 | 128 | function res = photos_getPopular(obj, varargin) 129 | %% Get popular photos 130 | method = 'flickr.photos.getPopular'; 131 | params = {'user_id', 'optional', '' 132 | 'sort', 'optional', 'interesting' 133 | 'extras', 'optional', '' 134 | 'per_page', 'optional', 100 135 | 'page', 'optional', 1 136 | 'getAll', 'apiOption', false}; 137 | res = obj.call_api(method, params, varargin); 138 | res = res.photos; 139 | res.photo = struct2table(res.photo, 'AsArray', 1); 140 | end 141 | 142 | function res = photos_getSizes(obj, photo_id, varargin) 143 | %% Get popular photos 144 | method = 'flickr.photos.getSizes'; 145 | params = {'photo_id', 'required', photo_id}; 146 | res = obj.call_api(method, params, varargin); 147 | res = res.sizes; 148 | res = struct2table(res.size, 'AsArray', 1); 149 | end 150 | 151 | function res = photos_search(obj, varargin) 152 | %% Get popular photos 153 | method = 'flickr.photos.search'; 154 | params = {'user_id', 'optional', '' 155 | 'tags', 'optional', '' 156 | 'tag_mode', 'optional', 'any' 157 | 'text', 'optional', '' 158 | 'sort', 'optional', 'date-posted-desc'}; 159 | res = obj.call_api(method, params, varargin); 160 | res = res.photos; 161 | res.photo = struct2table(res.photo, 'AsArray', 1); 162 | end 163 | 164 | function res = groups_getPhotos(obj, group_id, varargin) 165 | %% Get popular photos 166 | method = 'flickr.groups.pools.getPhotos'; 167 | params = {'group_id', 'required', group_id 168 | 'tags', 'optional', '' 169 | 'user_id', 'optional', '' 170 | 'extras', 'optional', '' 171 | 'per_page', 'optional', 100 172 | 'page', 'optional', 1 173 | 'getAll', 'apiOption', false}; 174 | res = obj.call_api(method, params, varargin); 175 | res = res.photos; 176 | res.photo = struct2table(res.photo, 'AsArray', 1); 177 | end 178 | 179 | function res = get_photo(obj, id, varargin) 180 | %% Download Photo 181 | params = {'id', 'required', id 182 | 'size', 'apiOption', 'thumbnail' 183 | 'show', 'apiOption', false 184 | 'save', 'apiOption', false 185 | 'name', 'apiOption', 'image.jpg'}; 186 | [~, apiopts] = obj.prepare_params(params, varargin); 187 | sizes = obj.photos_getSizes(id); 188 | i = strcmpi(apiopts.size, sizes.label); 189 | if any(i) 190 | res = webread(sizes.source{i}); 191 | else 192 | error('API Error: "%s" size is not available. Try another size', apiopts.size); 193 | end 194 | if apiopts.show 195 | P = WEB.Utils.Plotter(); 196 | P.image(res); 197 | end 198 | if apiopts.save 199 | obj.storage.imsave(apiopts.name, res); 200 | end 201 | end 202 | 203 | end 204 | end 205 | 206 | -------------------------------------------------------------------------------- /+WEB/HeadHunter.m: -------------------------------------------------------------------------------- 1 | classdef HeadHunter < WEB.API.Common 2 | % hh.ru API 3 | % https://github.com/hhru/api 4 | 5 | properties 6 | URL = 'https://api.hh.ru/' % Base URL 7 | access_token % Access Token 8 | TU = WEB.Utils.Tables 9 | end 10 | 11 | methods 12 | function obj = HeadHunter(access_token) 13 | %% VK Construct an instance of this class 14 | if nargin > 0 15 | obj.access_token = access_token; 16 | end 17 | end 18 | 19 | function [res, err] = call_api(obj, method, params, vars) 20 | %% Get via VK API 21 | if nargin < 3 22 | params = {}; 23 | vars = {}; 24 | end 25 | if ~isstruct(params) 26 | [params, apiopts] = obj.prepare_params(params, vars); 27 | end 28 | params = obj.fix_dates(params); 29 | req = WEB.API.Req(obj.URL); 30 | req.addurl(method); 31 | req.setquery(params); 32 | if ~isempty(obj.access_token) 33 | req.addheader('Authorization', "Bearer " + obj.access_token); 34 | end 35 | req.setopts('ContentType', 'json'); 36 | req.setopts('Timeout', 15); 37 | [res, err] = get(req); 38 | end 39 | 40 | function p = fix_dates(~, p) 41 | % Convert dates to ISO 8601 format 42 | if ~isempty(p) 43 | for f = fieldnames(p)' 44 | if ismember(f{1}, {'date_from', 'date_to'}) 45 | pval = p.(f{1}); 46 | if isdatetime(pval) 47 | p.(f{1}) = datestr(pval, 30); 48 | end 49 | end 50 | end 51 | end 52 | end 53 | 54 | function [res, err] = me(obj) 55 | %% Get self info 56 | method = 'me'; 57 | [res, err] = obj.call_api(method); 58 | end 59 | 60 | function [res, err] = dictionaries(obj) 61 | %% Get dictionaries 62 | method = 'dictionaries'; 63 | [res, err] = obj.call_api(method); 64 | res = orderfields(res); 65 | res = structfun(@struct2table, res, 'un', 0); 66 | end 67 | 68 | function [res, err] = industries(obj) 69 | %% Get industries 70 | method = 'industries'; 71 | [res, err] = obj.call_api(method); 72 | res = struct2table(res); 73 | end 74 | 75 | function [res, err] = specializations(obj) 76 | %% Get specializations 77 | method = 'specializations'; 78 | [res, err] = obj.call_api(method); 79 | res = struct2table(res); 80 | end 81 | 82 | function [res, err] = areas(obj) 83 | %% Get areas 84 | method = 'areas'; 85 | [res, err] = obj.call_api(method); 86 | res = struct2table(res); 87 | end 88 | 89 | function [res, err] = metro(obj) 90 | %% Get metro 91 | method = 'metro'; 92 | [res, err] = obj.call_api(method); 93 | res = struct2table(res); 94 | end 95 | 96 | function [res, err] = vacancies(obj, varargin) 97 | %% Get vacancies 98 | method = 'vacancies'; 99 | params = {'text', 'optional', '' 100 | 'search_field', 'optional', '' 101 | 'experience', 'optional', '' 102 | 'employment', 'optional', '' 103 | 'schedule', 'optional', '' 104 | 'area', 'optional', [] 105 | 'metro', 'optional', '' 106 | 'specialization', 'optional', '' 107 | 'industry', 'optional', '' 108 | 'employer_id', 'optional', '' 109 | 'currency', 'optional', '' 110 | 'salary', 'optional', '' 111 | 'label', 'optional', '' 112 | 'only_with_salary', 'optional', false 113 | 'period', 'optional', 30 114 | 'date_from', 'optional', '' 115 | 'date_to', 'optional', '' 116 | 'top_lat', 'optional', [] 117 | 'bottom_lat', 'optional', [] 118 | 'left_lng', 'optional', [] 119 | 'right_lng', 'optional', [] 120 | 'order_by', 'optional', '' 121 | 'sort_point_lat', 'optional', [] 122 | 'sort_point_lng', 'optional', [] 123 | 'clusters', 'optional', false 124 | 'describe_arguments', 'optional', false 125 | 'per_page', 'optional', 20 126 | 'page', 'optional', 0 127 | 'no_magic', 'optional', true 128 | 'premium', 'optional', false 129 | 'getAll', 'apiOption', false 130 | 'step', 'apiOption', 12 131 | 'batchSave', 'apiOption', false 132 | 'saveParams', 'apiOption', false 133 | 'toFile', 'apiOption', ''}; 134 | [params, apiopts] = obj.prepare_params(params, varargin); 135 | if apiopts.getAll 136 | k = 0; 137 | if isfield(params, 'date_to') 138 | d2 = params.date_to; 139 | else 140 | d2 = dateshift(datetime('now'), 'start', 'day', 'next'); 141 | end 142 | if isfield(params, 'date_from') 143 | d1 = params.date_from; 144 | else 145 | d1 = d2 - calmonths(1); 146 | end 147 | ds = d1 : hours(apiopts.step) : d2; 148 | ns = length(ds) - 1; 149 | items = []; 150 | if apiopts.batchSave 151 | if ~isfolder('data') 152 | mkdir('data'); 153 | end 154 | k = 0; 155 | end 156 | for n = 1 : ns 157 | p = 1; 158 | if apiopts.batchSave 159 | items = []; 160 | k = k + 1; 161 | end 162 | fprintf('%d/%d: page %d\n', n, ns, p); 163 | [res, err] = getBatch(obj, method, params, ds, n, p); 164 | if err 165 | disp("ERROR:" + newline + err) 166 | else 167 | items1 = obj.TU.concat(res.items); 168 | if isempty(items) 169 | items = items1; 170 | else 171 | items = obj.TU.concat({items items1}); 172 | end 173 | ps = res.pages; 174 | if ps > 1 175 | for p = 2 : ps 176 | fprintf('%d/%d: page %d/%d\n', n, ns, p, ps); 177 | if ps == 20 178 | warning('Bulk overflow, some data lost'); 179 | end 180 | [res, err] = getBatch(obj, method, params, ds, n, p); 181 | if err 182 | disp(err); 183 | else 184 | items1 = obj.TU.concat(res.items); 185 | if isempty(items) 186 | items = items1; 187 | else 188 | items = obj.TU.concat({items items1}); 189 | end 190 | end 191 | pause(0.05); 192 | end 193 | end 194 | end 195 | if apiopts.batchSave 196 | if ~isempty(items) 197 | if apiopts.saveParams 198 | data = struct('items', items, 'params', params, 'apiopts', apiopts); 199 | vartosave = 'data'; 200 | else 201 | vartosave = 'items'; 202 | end 203 | timestamp = datestr(datetime, 'yyyymmddhhMMssFFF'); 204 | save("data/data" + timestamp, vartosave); 205 | end 206 | end 207 | end 208 | if ~apiopts.batchSave 209 | res = obj.TU.unique(items, 'id'); 210 | end 211 | tof = apiopts.toFile; 212 | if tof 213 | if endsWith(tof, {'xlsx', 'xls', 'csv', 'txt'}) 214 | writetable(items, tof); 215 | else 216 | save(tof, 'items'); 217 | end 218 | end 219 | else 220 | [res, err] = obj.call_api(method, params); 221 | res.items = obj.extract(res, 'items'); 222 | end 223 | function [res, err] = getBatch(obj, method, params, ds, n, p) 224 | params.date_from = ds(n); 225 | params.date_to = ds(n + 1); 226 | params.per_page = 100; 227 | params.page = p - 1; 228 | [res, err] = obj.call_api(method, params); 229 | end 230 | end 231 | 232 | end 233 | end 234 | -------------------------------------------------------------------------------- /+WEB/IP.m: -------------------------------------------------------------------------------- 1 | classdef IP 2 | % IP-API - free IP geolocation API 3 | % http://ip-api.com 4 | 5 | properties 6 | URL = 'http://ip-api.com/json/' % Base URL 7 | end 8 | 9 | methods 10 | function obj = IP() 11 | %IP Construct an instance of this class 12 | end 13 | 14 | function [info, addr] = get(obj, target, opt) 15 | % Get my IP 16 | req = WEB.API.Req(obj.URL); 17 | if nargin > 1 18 | req.addurl(target); 19 | end 20 | req.setopts('Timeout', 10); 21 | info = get(req); 22 | if ~isempty(info) 23 | addr = info.query; 24 | if (nargin > 2) && strcmpi(opt, 'plot') 25 | obj.plot_geo(info); 26 | end 27 | else 28 | error('API Error: empty response') 29 | end 30 | end 31 | 32 | function g = plot_geo(~, info) 33 | %% Plot location in map 34 | P = WEB.Utils.Plotter; 35 | title = info.city + ", " + info.country; 36 | g = P.map([info.lat info.lon], 'Color', info.as, 'Title', title); 37 | g.ColorLegendTitle = 'Name'; 38 | end 39 | 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /+WEB/MPS.m: -------------------------------------------------------------------------------- 1 | classdef MPS < WEB.API.Common 2 | % MATLAB Production Server API 3 | 4 | properties 5 | addr % Server address with port 6 | app % MPS application 7 | outputFormat % output Format for synchronous request 8 | client % client ID for asynchronous request 9 | id % ID of asynchronous request 10 | self % URI of asynchronous request 11 | up % URI of asynchronous requests collection 12 | state % state of asynchronous request 13 | lastModifiedSeq % server state number 14 | end 15 | 16 | methods 17 | 18 | function obj = MPS(address, application) 19 | %% Template Construct an instance of this class 20 | obj.addr = address; 21 | obj.app = application; 22 | obj.setOutputFormat(); 23 | obj.client = char(java.net.InetAddress.getLocalHost.getHostName); 24 | end 25 | 26 | function setOutputFormat(obj, options) 27 | %% Set output format 28 | arguments 29 | obj 30 | options.mode char {mustBeMember(options.mode, {'small', 'large'})} = 'small' 31 | options.nanInfFormat char {mustBeMember(options.nanInfFormat, {'string', 'object'})} = 'object' 32 | end 33 | obj.outputFormat = options; 34 | end 35 | 36 | function [res, err] = health(obj) 37 | %% Check server health 38 | [res, err] = obj.get('api', 'health'); 39 | if err 40 | res = false; 41 | else 42 | res = true; 43 | end 44 | end 45 | 46 | function [res, err] = discovery(obj) 47 | %% Discovery methods 48 | [res, err] = obj.get('api', 'discovery'); 49 | end 50 | 51 | function [res, err] = exec(obj, fcn, rhs, nout) 52 | %% Execute synchronous request 53 | arguments 54 | obj 55 | fcn 56 | rhs = [] 57 | nout double = 1 58 | end 59 | body = obj.createBody(rhs, nout); 60 | [res, err] = obj.post(obj.app, fcn, body); 61 | if ~err 62 | res = mps.json.decoderesponse(res); 63 | end 64 | end 65 | 66 | function [res, err] = async(obj, fcn, rhs, nout) 67 | %% Asynchronous execution 68 | arguments 69 | obj 70 | fcn 71 | rhs = [] 72 | nout double = 1 73 | end 74 | body = obj.createBody(rhs, nout); 75 | query = struct('mode', 'async', 'client', obj.client); 76 | [res, err] = obj.post(obj.app, fcn, body, query); % call WEB API 77 | if ~err 78 | res = jsondecode(res); 79 | obj.id = res.id; 80 | obj.self = res.self; 81 | obj.up = res.up; 82 | obj.state = res.state; 83 | obj.lastModifiedSeq = res.lastModifiedSeq; 84 | end 85 | end 86 | 87 | function [res, err] = representation(obj) 88 | %% Get representation of asynchronous request 89 | [res, err] = obj.get(obj.self, ''); 90 | if ~err 91 | obj.self = res.self; 92 | obj.state = res.state; 93 | end 94 | end 95 | 96 | function [res, err] = collection(obj, since, clients, ids) 97 | %% Get collections of asynchronous requests 98 | arguments 99 | obj 100 | since double = obj.lastModifiedSeq 101 | clients = '' 102 | ids = '' 103 | end 104 | if isempty(clients) 105 | clients = obj.client; 106 | end 107 | if ~isempty(ids) 108 | query = struct('since', since, 'ids', obj.id); 109 | else 110 | query = struct('since', since, 'clients', clients); 111 | end 112 | [res, err] = obj.get(obj.up, '', query); 113 | end 114 | 115 | function [res, err] = information(obj) 116 | %% Get information about asynchronous requests 117 | [res, err] = obj.get(obj.self, 'info'); 118 | if ~err 119 | obj.lastModifiedSeq = res.lastModifiedSeq; 120 | obj.state = res.state; 121 | end 122 | end 123 | 124 | function [res, err, raw] = result(obj) 125 | %% Get asynchronous requests result 126 | [raw, err] = obj.get(obj.self, 'result'); 127 | if ~err 128 | if isstruct(raw) 129 | res = raw; 130 | else 131 | res = jsondecode(char(raw')); 132 | end 133 | res = res.lhs; 134 | end 135 | end 136 | 137 | function [res, err] = cancel(obj) 138 | %% Cancel asynchronous requests 139 | [res, err] = obj.post(obj.self, 'cancel'); 140 | end 141 | 142 | function [res, err] = delete(obj) 143 | %% Delete asynchronous request 144 | if ~isempty(obj.self) 145 | [res, err] = obj.delete_req(obj.self); 146 | obj.self = []; 147 | obj.id = []; 148 | obj.state = ''; 149 | end 150 | end 151 | 152 | function help(~) 153 | %% Open online documentation 154 | web('https://www.mathworks.com/help/mps/restful-api-and-json.html'); 155 | end 156 | 157 | end 158 | 159 | methods (Access = private) 160 | 161 | function [res, err] = get(obj, app, fcn, query) 162 | %% Perform get request 163 | req = WEB.API.Req(obj.addr); 164 | req.addurl(app); 165 | req.addurl(fcn); 166 | if nargin > 3 167 | req.addquery(query); 168 | end 169 | req.setopts('Timeout', obj.timeout); 170 | [res, err] = req.get(); 171 | end 172 | 173 | function [res, err] = post(obj, app, fcn, body, query) 174 | %% Execute synchronous request 175 | req = WEB.API.Req(obj.addr); 176 | req.addurl(app); 177 | req.addurl(fcn); 178 | if nargin > 4 179 | req.addquery(query); 180 | end 181 | if nargin > 3 182 | req.setbody(body); 183 | end 184 | req.setopts('ContentType', 'text'); 185 | req.setopts('MediaType', 'application/json'); 186 | req.setopts('Timeout', obj.timeout); 187 | [res, err] = post(req); 188 | end 189 | 190 | function [res, err] = delete_req(obj, uri) 191 | %% Perform get request 192 | req = WEB.API.Req(obj.addr); 193 | req.addurl(uri); 194 | req.setopts('Timeout', obj.timeout); 195 | [res, err] = req.call('delete'); 196 | end 197 | 198 | function body = createBody(obj, rhs, nout) 199 | %% Create body for post request 200 | if ~iscell(rhs) 201 | rhs = {rhs}; 202 | end 203 | body = mps.json.encoderequest(rhs, 'Nargout', nout,... 204 | 'OutputFormat', obj.outputFormat.mode,... 205 | 'OutputNanInfType', obj.outputFormat.nanInfFormat); 206 | end 207 | 208 | end 209 | 210 | end -------------------------------------------------------------------------------- /+WEB/NetSuite.m: -------------------------------------------------------------------------------- 1 | classdef NetSuite < WEB.API.Common 2 | % NetSuite WEB API 3 | 4 | properties (Access = private, Hidden = true) 5 | URL = 'https://%s.restlets.api.netsuite.com/app/site/hosting/restlet.nl' % Base URL 6 | account % Company Account ID 7 | consumer_key % Consumer Key 8 | consumer_secret % Consumer Secret 9 | token_id % Token ID 10 | token_secret % Token Secret 11 | sd_search = [149 1]; % [script deploy] 12 | sd_submit = [150 1]; % [script deploy] 13 | email % NLAuth Email 14 | password % NLAuth Password 15 | role % NLAuth Role 16 | TU = WEB.Utils.Tables % Tables utils 17 | end 18 | 19 | methods 20 | function obj = NetSuite(account, email, password, role, token_secret) 21 | %% NetSuite Construct an instance of this class 22 | obj.account = account; 23 | if nargin < 5 24 | obj.email = email; 25 | obj.password = password; 26 | obj.role = role; 27 | else 28 | obj.consumer_key = email; 29 | obj.consumer_secret = password; 30 | obj.token_id = role; 31 | obj.token_secret = token_secret; 32 | end 33 | end 34 | 35 | function url = getUrl(obj) 36 | %% Get request URL 37 | url = sprintf(obj.URL, obj.account); 38 | end 39 | 40 | function [res, err] = call_api(obj, sd, method, query, body) 41 | %% Call NetSuite API 42 | req = WEB.API.Req(obj.getUrl()); 43 | sd = string(sd); 44 | req.addquery('script', sd(1)); 45 | req.addquery('deploy', sd(2)); 46 | if nargin > 3 && ~isempty(query) 47 | req.addquery(query); 48 | end 49 | if nargin > 4 && ~isempty(body) 50 | req.addbody(body); 51 | end 52 | req.setopts('Timeout', obj.timeout); 53 | req.setopts('ArrayFormat', 'json'); 54 | req.setopts('ContentType', 'json'); 55 | req.setopts('MediaType', 'application/json'); 56 | req.addheader('Content-type', 'application/json'); 57 | req = obj.auth(req, method); 58 | [res, err] = req.call(method); 59 | end 60 | 61 | function [resp, err] = search(obj, type, columns, filters, varargin) 62 | %% NetSuite search 63 | params = { 64 | 'type', 'required', type 65 | 'columns', 'required', columns 66 | 'filters', 'required', filters 67 | 'AsTable', 'apiOption', false 68 | }; 69 | [params, apiopts] = obj.prepare_params(params, varargin); 70 | query = {'type', type 71 | 'columns', jsonencode(columns) 72 | 'filters', jsonencode(filters)}; 73 | [resp, err] = obj.call_api(obj.sd_search, 'GET', query); 74 | if ~isempty(resp) && apiopts.AsTable 75 | resp = struct2table(resp, 'AsArray', true); 76 | resp = obj.TU.expand(resp, 'values'); 77 | end 78 | end 79 | 80 | function [resp, err] = submit(obj, type, ids, values) 81 | %% NetSuite record submit 82 | ids = str2double(ids); 83 | if isscalar(ids) 84 | ids = {ids}; 85 | end 86 | body = {'type', type 87 | 'ids', jsonencode(ids) 88 | 'values', jsonencode(values)}; 89 | [resp, err] = obj.call_api(obj.sd_submit, 'PUT', [], body); 90 | end 91 | 92 | function [resp, err] = post(obj, script, deploy, body) 93 | %% Send data 94 | [resp, err] = obj.call_api([script deploy], 'POST', [], body); 95 | end 96 | 97 | function [resp, err] = getEmployee(obj, email) 98 | %% Get Employee Name 99 | [resp, err] = obj.search('employee', {'firstname' 'lastname'}, {'email' 'is' email}); 100 | if ~err 101 | resp = obj.extractVal(resp); 102 | end 103 | end 104 | 105 | function req = auth(obj, req, method) 106 | %% Set Auth paramaters 107 | if ~isempty(obj.email) && ~isempty(obj.password) 108 | req = obj.nlauth(req); 109 | else 110 | req = obj.oauth(req, method); 111 | end 112 | end 113 | 114 | function req = nlauth(obj, req) 115 | %% NLAuth method 116 | str = 'NLAuth nlauth_account=%s,nlauth_email=%s,nlauth_signature=%s,nlauth_role=%d'; 117 | authstr = sprintf(str, obj.account, obj.email, obj.password, obj.role); 118 | req.addheader('Authorization', authstr); 119 | end 120 | 121 | function req = oauth(obj, req, method) 122 | %% OAuth1.0 123 | a.oauth_version = '1.0'; 124 | a.oauth_consumer_key = obj.consumer_key; 125 | a.oauth_token = obj.token_id; 126 | a.oauth_signature_method = 'HMAC-SHA1'; 127 | a.secret = {obj.consumer_secret obj.token_secret}; 128 | a.realm = obj.account; 129 | a.authtype = 'header'; 130 | A = WEB.API.Auth(req, a, method); 131 | req = A.oauth10(); 132 | end 133 | 134 | function val = extractVal(~, res) 135 | %% Extract Values 136 | if isfield(res, 'values') 137 | val = res.values; 138 | else 139 | warning('Result has no values'); 140 | val = []; 141 | end 142 | end 143 | 144 | end 145 | end -------------------------------------------------------------------------------- /+WEB/OpenWeatherMap.m: -------------------------------------------------------------------------------- 1 | classdef OpenWeatherMap < WEB.API.Common 2 | % Bing Maps 3 | % https://msdn.microsoft.com/en-us/library/ff701713.aspx 4 | 5 | properties 6 | URL = "http://api.openweathermap.org/data/2.5/" % Base URL 7 | key % API Key 8 | end 9 | 10 | methods 11 | function obj = OpenWeatherMap(api_key) 12 | %% OpenWeatherMap Construct an instance of this class 13 | obj.key = api_key; 14 | end 15 | 16 | function [res, apiopts] = call_api(obj, method, params, vars) 17 | %% Get via API 18 | [params, apiopts] = obj.prepare_params(params, vars); 19 | req = WEB.API.Req(obj.URL); 20 | req.addurl(method); 21 | req.setquery(params); 22 | req.addquery('APPID', obj.key); 23 | req.setopts('Timeout', obj.timeout); 24 | res = get(req); 25 | end 26 | 27 | function res = current(obj, varargin) 28 | %% Get current weather 29 | method = 'weather'; 30 | params = {'q', 'optional', '' 31 | 'id', 'optional', [] 32 | 'lat', 'optional', [] 33 | 'lon', 'optional', [] 34 | 'zip', 'optional', '' 35 | 'units', 'optional', 'default' 36 | 'show', 'apiOption', false}; 37 | [res, apiopts] = obj.call_api(method, params, varargin); 38 | if apiopts.show 39 | obj.show_weather(res); 40 | end 41 | end 42 | 43 | function [res, data] = forecast(obj, varargin) 44 | %% Get 5 day / 3 hour forecast data 45 | method = 'forecast'; 46 | params = {'q', 'optional', '' 47 | 'id', 'optional', [] 48 | 'lat', 'optional', [] 49 | 'lon', 'optional', [] 50 | 'zip', 'optional', '' 51 | 'units', 'optional', 'default' 52 | 'show', 'apiOption', false}; 53 | [res, apiopts] = obj.call_api(method, params, varargin); 54 | TU = WEB.Utils.Tables; 55 | data = TU.concat(res.list); 56 | if isstruct(data) 57 | data = struct2table(data, 'AsArray', true); 58 | end 59 | data.dt = datetime(data.dt, 'ConvertFrom', 'posixtime'); 60 | if apiopts.show 61 | obj.plot_data(data); 62 | end 63 | end 64 | 65 | function ax = show_weather(~, res) 66 | %% Plot weather 67 | [img, ~, ~] = imread(['http://openweathermap.org/img/w/' res.weather.icon '.png']); 68 | P = WEB.Utils.Plotter(); 69 | ax = P.image(img); 70 | text(3, 5, num2str(res.name), 'FontSize', 40); 71 | text(3, 43, num2str(res.main.temp) + "^o", 'FontSize', 50); 72 | text(36, 44, num2str(res.main.humidity) + "%", 'FontSize', 40, 'Color', 'b'); 73 | end 74 | 75 | function plot_data(~, data) 76 | %% Plot weather forecast 77 | figure; 78 | % Plot weather 79 | subplot(8, 1, 1) 80 | icons = {data.weather.icon}; 81 | uicons = unique(icons); 82 | cm = containers.Map; 83 | for i = uicons 84 | [img, ~, ~] = imread(['http://openweathermap.org/img/w/' i{1} '.png']); 85 | cm(i{1}) = img; 86 | end 87 | w = []; 88 | for i = icons 89 | w = [w cm(i{1})]; 90 | end 91 | imshow(w) 92 | title('Weather') 93 | % Plot temperature 94 | subplot(8, 1, [2 4]) 95 | plot(data.dt, [data.main.temp]); 96 | hold on 97 | plot(data.dt, [data.main.temp_min], 'b:'); 98 | plot(data.dt, [data.main.temp_max], 'r:'); 99 | hold off 100 | title('Temperature'); 101 | grid on 102 | % Plot humidity 103 | subplot(8, 1, [6 8]) 104 | plot(data.dt, [data.main.humidity]); 105 | title('Humidity'); 106 | grid on 107 | end 108 | end 109 | end 110 | 111 | -------------------------------------------------------------------------------- /+WEB/README.md: -------------------------------------------------------------------------------- 1 | [English](#web-api-description-gb) | [Русский](#Описание-web-api-ru) 2 | 3 | # WEB API Description :gb: 4 | - **+API** - core WEB APIs components (WEB requests processing, authentication, API building) 5 | - **+Utils** - additional tools (data processing, visualization, etc.) 6 | ## Realized APIs 7 | (*Rediness:* :new_moon: :waning_crescent_moon: :last_quarter_moon: :waning_gibbous_moon: :full_moon:) 8 | - **BingMaps.m** - [Bing Maps](https://msdn.microsoft.com/en-us/library/ff701713.aspx) mapping service API :waning_crescent_moon: [(example)](../examples/bingmaps_example.m) 9 | - **DataGov.m** - [Open Data of Russian Federation](https://data.gov.ru/pravila-i-rekomendacii) API :waning_gibbous_moon: [(example)](../examples/datagov_example.m) 10 | - **Flickr.m** - [Flickr.com](https://www.flickr.com/services/api) photo hosting API :waning_crescent_moon: [(example)](../examples/flickr_example.m) 11 | - **HeadHunter.m** - Russian recruiting service [hh.com](https://dev.hh.ru) API :waning_crescent_moon: [(example)](../examples/headhunter_example.m) 12 | - **IP.m** - [IP](http://ip-api.com/) geolocation API :full_moon: [(example)](../examples/ip_example.m) 13 | - **NetSuite.m** - [NetSuite](http://netsuite.com/) CRM RESTlets API :last_quarter_moon: [(example)](../examples/netsuite_example.m) 14 | - **OpenWeatherMap.m** - [OpenWeatherMap](https://openweathermap.org/api) weather forecasting API :waning_crescent_moon: [(example)](../examples/openweathermap_example.m) 15 | - **RESTCountries.m** - [Countries](http://restcountries.eu/) information API :waning_gibbous_moon: [(example)](../examples/restcountries_example.m) 16 | - **Template.m** - New WEB API template :new_moon: 17 | - **Uinames.m** - Random names generator [uinames.com](https://uinames.com/) :waning_gibbous_moon: [(example)](../examples/uinames_example.m) 18 | - **VK.m** - Popular russian social network [VK.com](https://vk.com/dev/manuals) API :waning_crescent_moon: [(example)](../examples/vk_example.m) 19 | - **YouTube.m** - Video hosting [YouTube.com](https://www.youtube.com/yt/dev/) API :waning_crescent_moon: [(example)](../examples/youtube_example.m) 20 | - **MPS.m** - [MATLAB Production Server](https://www.mathworks.com/products/matlab-production-server.html) API :last_quarter_moon: [(example)](../examples/mps_example.m) 21 | - **TelegramBot.m** - [Telegram Bot API](https://core.telegram.org/bots/api) :waning_crescent_moon: 22 | 23 | # Описание WEB API :ru: 24 | - **+API** - ключевые компоненты WEB API (создание и обработка запросов, авторизация, построение API) 25 | - **+Utils** - дополнительные библиотеки (обработка данных, визуализация и т.д.) 26 | ## Реализованные API 27 | (*Степень готовности:* :new_moon: :waning_crescent_moon: :last_quarter_moon: :waning_gibbous_moon: :full_moon:) 28 | - **BingMaps.m** - API для работы с картами [Bing Maps](https://msdn.microsoft.com/en-us/library/ff701713.aspx) :waning_crescent_moon: [(пример)](../examples/bingmaps_example.m) 29 | - **DataGov.m** - API для работы с [Открытыми Данными России](https://data.gov.ru/pravila-i-rekomendacii) :waning_gibbous_moon: [(пример)](../examples/datagov_example.m) 30 | - **Flickr.m** - API для работы с фотохостингом [Flickr.com](https://www.flickr.com/services/api) :waning_crescent_moon: [(пример)](../examples/flickr_example.m) 31 | - **HeadHunter.m** - API для работы с российским сервисом рекрутинга [hh.com](https://dev.hh.ru) :waning_crescent_moon: [(пример)](../examples/headhunter_example.m) 32 | - **IP.m** - API для определения местоположения по [IP](http://ip-api.com/) :full_moon: [(пример)](../examples/ip_example.m) 33 | - **NetSuite.m** - API для работы с рестлетами CRM [NetSuite](http://netsuite.com/) :last_quarter_moon: [(пример)](../examples/netsuite_example.m) 34 | - **OpenWeatherMap.m** - API для работы с сервисом погоды [OpenWeatherMap](https://openweathermap.org/api) :waning_crescent_moon: [(пример)](../examples/openweathermap_example.m) 35 | - **RESTCountries.m** - API для получения подробной информации о [странах](http://restcountries.eu/) :waning_gibbous_moon: [(пример)](../examples/restcountries_example.m) 36 | - **Template.m** - Шаблон для создания нового WEB API :new_moon: 37 | - **Uinames.m** - Генератор случайных имён [uinames.com](https://uinames.com/) :waning_gibbous_moon: [(пример)](../examples/uinames_example.m) 38 | - **VK.m** - API для работы с популярной российской соцсетью [VK.com](https://vk.com/dev/manuals) :waning_crescent_moon: [(пример)](../examples/vk_example.m) 39 | - **YouTube.m** - API видео-хостинга [YouTube.com](https://www.youtube.com/yt/dev/) :waning_crescent_moon: [(пример)](../examples/youtube_example.m) 40 | - **MPS.m** - [MATLAB Production Server](https://www.mathworks.com/products/matlab-production-server.html) API :last_quarter_moon: [(пример)](../examples/mps_example.m) 41 | - **TelegramBot.m** - [Telegram Bot API](https://core.telegram.org/bots/api) :waning_crescent_moon: -------------------------------------------------------------------------------- /+WEB/RESTCountries.m: -------------------------------------------------------------------------------- 1 | classdef RESTCountries < WEB.API.Common 2 | % Get information about countries via a RESTful API 3 | % http://restcountries.eu 4 | 5 | properties 6 | URL = 'https://restcountries.eu/rest/v2/' % Base URL 7 | end 8 | 9 | methods 10 | function obj = RESTCountries() 11 | % RESTCountries Construct an instance of this class 12 | end 13 | 14 | function [res, apiopts] = call_api(obj, method, params, vars) 15 | %% Call WEB API 16 | req = WEB.API.Req(obj.URL); 17 | req.addurl(method); 18 | if ~isempty(params) 19 | [params, apiopts] = obj.prepare_params(params, vars); 20 | req.setquery(params); 21 | end 22 | req.setopts('Timeout', obj.timeout); 23 | res = get(req); 24 | if ~isempty(res) && ~isscalar(res) 25 | if isstruct(res) 26 | res = struct2table(res); 27 | else 28 | TU = WEB.Utils.Tables; 29 | res = TU.concat(res); 30 | end 31 | end 32 | end 33 | 34 | function res = all(obj, varargin) 35 | %% Get info about all countries 36 | method = 'all'; 37 | params = {'fields', 'optional', ''}; 38 | res = obj.call_api(method, params, varargin); 39 | end 40 | 41 | function res = byName(obj, name, varargin) 42 | %% Search by country name. It can be the native name or partial or full name 43 | method = "name/" + name; 44 | params = {'name', 'required', name 45 | 'fullText', 'optional', false 46 | 'fields', 'optional', ''}; 47 | res = obj.call_api(method, params, varargin); 48 | end 49 | 50 | function res = byCode(obj, codes, varargin) 51 | %% Search by ISO 3166-1 2-letter or 3-letter country code 52 | method = 'alpha'; 53 | codes = string(codes); 54 | if ~isscalar(codes) 55 | codes = join(codes, ';'); 56 | end 57 | params = {'codes', 'optional', codes 58 | 'fields', 'optional', ''}; 59 | vars = {'codes', codes}; 60 | res = obj.call_api(method, params, vars); 61 | end 62 | 63 | function res = byCurrency(obj, curcode, varargin) 64 | %% Search by ISO 4217 currency code 65 | method = "currency/" + curcode; 66 | params = {'fields', 'optional', ''}; 67 | res = obj.call_api(method, params, varargin); 68 | end 69 | 70 | function res = byRegion(obj, region, varargin) 71 | %% Search by region 72 | method = "region/" + region; 73 | params = {'fields', 'optional', ''}; 74 | res = obj.call_api(method, params, varargin); 75 | end 76 | 77 | end 78 | end 79 | 80 | -------------------------------------------------------------------------------- /+WEB/TelegramBot.m: -------------------------------------------------------------------------------- 1 | classdef TelegramBot < WEB.API.Common 2 | % Telegram Bot API 3 | 4 | properties 5 | URL = "https://api.telegram.org/" % Base URL 6 | token % Bot token 7 | end 8 | 9 | methods 10 | function obj = TelegramBot(token) 11 | %% Constructor 12 | obj.token = token; 13 | end 14 | 15 | function [res, err] = call_api(obj, method, params, vars) 16 | %% Call Bot API 17 | [params, apiopts] = obj.prepare_params(params, vars); % prepare call parameters and options 18 | % params - API method parameters (will be added to HTTP request) 19 | % apiopts - API options (wil NOT be added to HTTP request. Use it for better user experience if you want) 20 | req = WEB.API.Req(obj.URL); % new WEB Request 21 | req.addurl("bot" + obj.token); % add bot token 22 | req.addurl(method); % add API method 23 | req.setquery(params); % add method parameters 24 | req.setopts('ContentType', 'json'); % MATLAB works with JSON data 25 | req.setopts('Timeout', obj.timeout); % for heavy calls 26 | [res, err] = get(req); % call WEB API 27 | if ~err 28 | if ~res.ok 29 | err = res; 30 | res = []; 31 | end 32 | end 33 | end 34 | 35 | function [res, err] = getMe(obj) 36 | %% Get bot information 37 | method = 'getMe'; 38 | [res, err] = obj.call_api(method, [], []); 39 | end 40 | 41 | function [res, err] = getUpdates(obj, varargin) 42 | %% Get last updates 43 | method = 'getUpdates'; % WEB API Method (see WEB API documentation) 44 | params = { 45 | 'ofset', 'optional', 0 46 | 'limit', 'optional', 100 47 | 'timeout', 'optional', 0 48 | 'allowed_updates', 'optional', [] 49 | }; 50 | [res, err] = obj.call_api(method, params, varargin); 51 | end 52 | 53 | function [res, err] = sendMessage(obj, chat_id, text, varargin) 54 | %% Send message from bot 55 | method = 'sendMessage'; 56 | params = { 57 | 'chat_id', 'required', chat_id 58 | 'text', 'required', text 59 | 'parse_mode', 'optional', '' 60 | 'disable_web_page_preview', 'optional', false 61 | 'disable_notification', 'optional', false 62 | 'reply_to_message_id', 'optional', [] 63 | 'reply_markup', 'optional', '' 64 | }; 65 | [res, err] = obj.call_api(method, params, varargin); 66 | end 67 | 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /+WEB/Template.m: -------------------------------------------------------------------------------- 1 | classdef Template < WEB.API.Common 2 | % Template Class for your new API 3 | 4 | properties 5 | URL = "https://example.com/api/" % Base URL 6 | api_key = 'example_key' % API Key (if required) 7 | storage % Data Storage (if you need) 8 | end 9 | 10 | methods 11 | function obj = Template(api_key) 12 | %% Template Construct an instance of this class 13 | obj.api_key = api_key; 14 | obj.storage = WEB.Utils.Storage('template_data.mat'); 15 | end 16 | 17 | function set_data_path(obj, path) 18 | %% Set data storage path 19 | obj.storage.path = path; 20 | end 21 | 22 | function [res, err] = call_api(obj, method, params, vars) 23 | %% Call Template API - common method 24 | [params, apiopts] = obj.prepare_params(params, vars); % prepare call parameters and options 25 | % params - API method parameters (will be added to HTTP request) 26 | % apiopts - API options (wil NOT be added to HTTP request. Use it for better user experience if you want) 27 | req = WEB.API.Req(obj.URL); % new WEB Request 28 | req.addurl(method); % add API method 29 | req.setquery(params); % add method parameters 30 | req.setopts('ContentType', 'json'); % MATLAB works with JSON data 31 | req.setopts('Timeout', obj.timeout); % for heavy calls 32 | [res, err] = get(req); % call WEB API 33 | if apiopts.checkerr % check API auxiliary option 34 | obj.check_api_error(res); % check for WEB API errors if you need 35 | end 36 | end 37 | 38 | function check_api_error(~, res) 39 | %% Check API Call Error 40 | if isempty(res) 41 | error('API error: response is empty'); 42 | elseif isfield(res, 'error') 43 | error(['API error: ' res.error.error_msg]); 44 | end 45 | end 46 | 47 | function [res, err] = method1(obj, p1, varargin) 48 | %% Method1 example 49 | method = 'meth1'; % WEB API Method (see WEB API documentation) 50 | params = {'p1', 'required', p1 % required HTTP request parameter 51 | 'p2', 'optional', '' % optional HTTP request parameter 52 | 'p3', 'optional', 100 % optional HTTP request parameter 53 | 'checkErr', 'apiOption', true}; % auxiliary API option 54 | [res, err] = obj.call_api(method, params, varargin); % call API 55 | end 56 | 57 | function [res, err] = method2(obj, varargin) 58 | %% Method2 example 59 | method = 'meth2'; 60 | params = {'p1', 'optional', 0}; % Only one optional parameter 61 | [res, err] = obj.call_api(method, params, varargin); 62 | end 63 | 64 | function [res, err] = method3(obj, varargin) 65 | %% Method3 example 66 | method = 'meth3'; 67 | % Method has no parameters 68 | [res, err] = obj.call_api(method, [], []); 69 | end 70 | 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /+WEB/Uinames.m: -------------------------------------------------------------------------------- 1 | classdef Uinames < WEB.API.Common 2 | % Get information about countries via a RESTful API 3 | % http://restcountries.eu 4 | 5 | properties 6 | URL = 'http://uinames.com/api/' % Base URL 7 | end 8 | 9 | methods 10 | function obj = Uinames() 11 | % Uinames Construct an instance of this class 12 | end 13 | 14 | function [res, apiopts] = get(obj, varargin) 15 | %% Call uinames API 16 | params = {'amount', 'optional', 1 17 | 'gender', 'optional', '' 18 | 'region', 'optional', '' 19 | 'minlen', 'optional', 0 20 | 'maxlen', 'optional', 0}; 21 | req = WEB.API.Req(obj.URL); 22 | if ~isempty(params) 23 | [params, apiopts] = obj.prepare_params(params, varargin); 24 | req.setquery(params); 25 | end 26 | req.setopts('Timeout', obj.timeout); 27 | res = get(req); 28 | if ~isempty(res) 29 | res = struct2table(res, 'AsArray', true); 30 | end 31 | end 32 | 33 | end 34 | end 35 | 36 | -------------------------------------------------------------------------------- /+WEB/VK.m: -------------------------------------------------------------------------------- 1 | classdef VK < WEB.API.Common 2 | % VK.com API 3 | % https://vk.com/dev/manuals 4 | 5 | properties 6 | URL = "https://api.vk.com/method/" % Base URL 7 | client_id % Client ID 8 | client_secret % Client Secret 9 | scope % Scope 10 | ver = '5.120' % API Version 11 | authdata % Authentication Data 12 | end 13 | 14 | methods 15 | function obj = VK(client_id, scope, client_secret) 16 | %% VK Construct an instance of this class 17 | obj.client_id = client_id; 18 | obj.authdata = WEB.Utils.Storage('vk_auth_data.mat'); 19 | if isnumeric(scope) 20 | obj.scope = scope; 21 | else 22 | obj.scope = obj.get_scope(scope); 23 | end 24 | if nargin > 2 25 | obj.client_secret = client_secret; 26 | end 27 | end 28 | 29 | function set_data_path(obj, path) 30 | %% Set auth data path 31 | obj.authdata.path = path; 32 | end 33 | 34 | function scope = get_scope(~, names) 35 | %% Get scope value 36 | s = struct('notify',1,'friends',2,'photos',4,'audio',8,'video',16,... 37 | 'stories',64,'pages',128,'app',256,'status',1024,'notes',2048,... 38 | 'messages',4096,'wall',8192,'ads',32768,'offline',65536,'docs',131072,... 39 | 'groups',262144,'notifications',524288,'stats',1048576,'email',4194304,... 40 | 'market',134217728); 41 | names = cellstr(names); 42 | if strcmpi(names, 'all') 43 | scope = sum(struct2array(s)); 44 | else 45 | ss = cellfun(@(f)getfield(s,f), names, 'un', 0); %#ok 46 | scope = sum(cell2mat(ss)); 47 | end 48 | end 49 | 50 | function [res, count, err] = call_api(obj, method, params, vars) 51 | %% Call VK API 52 | [params, apiopts] = obj.prepare_params(params, vars); 53 | if isfield(apiopts, 'getAll') 54 | getAll = apiopts.('getAll'); 55 | else 56 | getAll = false; 57 | end 58 | fs = fieldnames(params); 59 | for i = 1 : length(fs) 60 | if isdatetime(params.(fs{i})) 61 | params.(fs{i}) = posixtime(params.(fs{i})); 62 | end 63 | end 64 | req = WEB.API.Req(obj.URL); 65 | req.addurl(method); 66 | if isfield(apiopts, 'httpPost') && apiopts.httpPost 67 | params = obj.decode_params(params, ["message" "name" "description"]); 68 | req.setbody(params, 1); 69 | req.addbody('v', obj.ver); 70 | req.setopts('MediaType', 'application/x-www-form-urlencoded'); 71 | A = WEB.API.Auth(req, obj.authdata.data); 72 | A.oauth20(); 73 | req.addbody('access_token', req.getquery('access_token')); 74 | req.clearquery(); 75 | [res, err] = post(req); 76 | if ~isempty(res) 77 | res = res.Body.Data; 78 | end 79 | else 80 | req.setquery(params); 81 | req.addquery('v', obj.ver); 82 | req.setopts('ContentType', 'json'); 83 | req.setopts('Timeout', obj.timeout); 84 | A = WEB.API.Auth(req, obj.authdata.data); 85 | req = A.oauth20(); 86 | [res, err] = get(req); 87 | end 88 | count = []; 89 | obj.check_api_error(res); 90 | extract = isfield(apiopts, 'extract') && apiopts.extract; 91 | if extract 92 | [res, count] = obj.extract(res.response, ["items", "users"]); 93 | end 94 | if getAll && (count >= 1000) 95 | % Get all items 96 | addRes = res; 97 | offset = 0; 98 | while ~isempty(addRes) 99 | pause(0.3); 100 | offset = offset + 1000; 101 | req.addquery('offset', offset); 102 | addRes = get(req); 103 | if extract 104 | addRes = obj.extract(addRes.response, ["items", "users"]); 105 | end 106 | res = [res; addRes]; 107 | end 108 | end 109 | TU = WEB.Utils.Tables(); 110 | res = TU.concat(res); % concatenate geterogeneous results 111 | end 112 | 113 | function check_api_error(~, resp) 114 | %% Check API Call Error 115 | if isfield(resp, 'error') 116 | error(['API error: ' resp.error.error_msg]); 117 | end 118 | end 119 | 120 | function auth_data = login(obj, opt) 121 | %% Authorization 122 | if (nargin > 1) 123 | auth_data = []; 124 | else 125 | auth_data = obj.load_auth_data(); 126 | end 127 | if isempty(auth_data) 128 | a.client_id = obj.client_id; 129 | a.scope = obj.scope; 130 | a.redirect_uri = 'https://oauth.vk.com/blank.html'; 131 | a.display = 'page'; 132 | a.v = obj.ver; 133 | if (nargin > 1) && strcmpi(opt, 'implicit') 134 | % Implicit flow 135 | req = WEB.API.Req('https://oauth.vk.com/authorize'); 136 | req.addquery('response_type', 'token'); 137 | A = WEB.API.Auth(req, a); 138 | auth_data = A.gettoken20('implicit'); 139 | else 140 | % Authorization Code Flow 141 | req1 = WEB.API.Req('https://oauth.vk.com/authorize'); 142 | req1.addquery('response_type', 'code'); 143 | req2 = WEB.API.Req('https://oauth.vk.com/access_token'); 144 | A = WEB.API.Auth([req1 req2], a); 145 | auth_data = A.gettoken20('code', obj.client_secret); 146 | end 147 | if ~isempty(auth_data) 148 | obj.authdata.save(auth_data); 149 | else 150 | error('VK API Error: Unsucessfull Authorization'); 151 | end 152 | end 153 | end 154 | 155 | function data = load_auth_data(obj) 156 | %% Load and verify authorization data from file 157 | data = obj.authdata.load; 158 | if ~isempty(data) 159 | expires_in = data.expires_in; 160 | expired = (expires_in ~= 0) && datetime('now') < (data.date + seconds(expires_in) - hours(1)); 161 | if ~isfield(data, 'access_token') || isempty(data.access_token) || expired 162 | data = []; 163 | obj.authdata.data = data; 164 | end 165 | end 166 | end 167 | 168 | function logout(obj) 169 | %% Delete auth data 170 | obj.authdata.clear(); 171 | end 172 | 173 | function [res, count] = friends_get(obj, user_id, varargin) 174 | %% Get user friends 175 | method = 'friends.get'; 176 | params = {'user_id', 'required', user_id 177 | 'fields', 'optional', '' 178 | 'offset', 'optional', 0 179 | 'count', 'optional', 5000 180 | 'getAll', 'apiOption', false 181 | 'extract', 'apiOption', true}; 182 | [res, count] = obj.call_api(method, params, varargin); 183 | end 184 | 185 | function [res, count] = groups_getMembers(obj, group_id, varargin) 186 | %% Get group members 187 | method = 'groups.getMembers'; 188 | params = {'group_id', 'required', group_id 189 | 'fields', 'optional', '' 190 | 'filter', 'optional', '' 191 | 'sort', 'optional', 'id_asc' 192 | 'offset', 'optional', 0 193 | 'count', 'optional', 1000; 194 | 'getAll', 'apiOption', 0 195 | 'extract', 'apiOption', true}; 196 | [res, count] = obj.call_api(method, params, varargin); 197 | end 198 | 199 | function [res, count] = groups_search(obj, q, varargin) 200 | %% Search groups 201 | method = 'groups.search'; 202 | params = {'q', 'required', q 203 | 'type', 'optional', 'group,page,event' 204 | 'sort', 'optional', 0 205 | 'offset', 'optional', 0 206 | 'count', 'optional', 1000 207 | 'extract', 'apiOption', true}; 208 | [res, count] = obj.call_api(method, params, varargin); 209 | end 210 | 211 | function [res, count] = groups_getById(obj, id, fields, varargin) 212 | %% Search groups 213 | method = 'groups.getById'; 214 | params = {'id', 'required', id 215 | 'fields', 'required', fields 216 | 'extract', 'apiOption', true 217 | 'httpPost', 'apiOption', false}; 218 | if isscalar(id) 219 | params{1, 1} = 'group_id'; 220 | params{1, 3} = string(params{1, 3}); 221 | else 222 | params{1, 1} = 'group_ids'; 223 | params{1, 3} = join(string(params{1, 3}), ','); 224 | end 225 | [res, count] = obj.call_api(method, params, varargin); 226 | end 227 | 228 | function [res, count] = users_getFollowers(obj, user_id, varargin) 229 | %% Get user followers 230 | method = 'users.getFollowers'; 231 | params = {'user_id', 'required', user_id 232 | 'fields', 'optional', '' 233 | 'name_case', 'optional', 'nom' 234 | 'offset', 'optional', 0 235 | 'count', 'optional', 1000 236 | 'getAll', 'apiOption', false 237 | 'extract', 'apiOption', true}; 238 | [res, count] = obj.call_api(method, params, varargin); 239 | end 240 | 241 | function [res, count] = users_getSubscriptions(obj, user_id, varargin) 242 | %% Get user subscriptions 243 | method = 'users.getSubscriptions'; 244 | params = {'user_id', 'required', user_id 245 | 'fields', 'optional', '' 246 | 'offset', 'optional', 0 247 | 'count', 'optional', 200 248 | 'extended', 'optional', true 249 | 'getAll', 'apiOption', false 250 | 'extract', 'apiOption', true}; 251 | [res, count] = obj.call_api(method, params, varargin); 252 | end 253 | 254 | function [res, err] = wall_post(obj, owner_id, varargin) 255 | %% Post on wall 256 | method = 'wall.post'; 257 | params = {'owner_id', 'required', owner_id 258 | 'message', 'optional', '' 259 | 'attachments', 'optional', '' 260 | 'friends_only', 'optional', 0 261 | 'httpPost', 'apiOption', false}; 262 | [res, ~, err] = obj.call_api(method, params, varargin); 263 | end 264 | 265 | function [res, err] = video_save(obj, varargin) 266 | %% Post on wall 267 | method = 'video.save'; 268 | params = {'name', 'optional', '' 269 | 'description', 'optional', '' 270 | 'is_private', 'optional', 0 271 | 'wallpost', 'optional', 0 272 | 'link', 'optional', '' 273 | 'httpPost', 'apiOption', false}; 274 | [res, ~, err] = obj.call_api(method, params, varargin); 275 | uploaded = webread(res.response.upload_url); 276 | if ~uploaded.response 277 | error('Video was not uploaded to VK'); 278 | end 279 | res = res.response; 280 | end 281 | 282 | function res = video_addToAlbum(obj, owner_id, video_id, varargin) 283 | %% Add video to album 284 | method = 'video.addToAlbum'; 285 | params = {'owner_id', 'required', owner_id 286 | 'video_id', 'required', video_id 287 | 'album_id', 'optional', [] 288 | 'album_ids', 'optional', [] 289 | 'target_id', 'optional', []}; 290 | [res, ~, err] = obj.call_api(method, params, varargin); 291 | res = res.response; 292 | end 293 | 294 | function [res, err] = video_get(obj, varargin) 295 | %% Get video 296 | method = 'video.get'; 297 | params = {'owner_id', 'optional', 0 298 | 'videos', 'optional', '' 299 | 'album_id', 'optional', 0 300 | 'count', 'optional', 100 301 | 'offset', 'optional', 0 302 | 'extended', 'optional', 0 303 | 'extract', 'apiOption', true}; 304 | p = obj.prepare_params(params, varargin); 305 | [res, ~, err] = obj.call_api(method, params, varargin); 306 | end 307 | 308 | function [res, err] = notifications_get(obj, varargin) 309 | %% Get notifications 310 | method = 'notifications.get'; 311 | params = {'count', 'optional', 30 312 | 'start_from', 'optional', '' 313 | 'filters', 'optional', '' 314 | 'start_time', 'optional', [] 315 | 'end_time', 'optional', [] 316 | 'extract', 'apiOption', true}; 317 | p = obj.prepare_params(params, varargin); 318 | [res, ~, err] = obj.call_api(method, params, varargin); 319 | end 320 | 321 | end 322 | 323 | methods (Access = private, Static) 324 | 325 | function params = decode_params(params, param_list) 326 | %% Fix parameters encoding 327 | arguments 328 | params 329 | param_list string 330 | end 331 | req = WEB.API.Req; 332 | for i = 1 : length(param_list) 333 | p = param_list(i); 334 | if isfield(params, p) 335 | params.(p) = req.decode(params.(p)); 336 | end 337 | end 338 | end 339 | 340 | end 341 | end -------------------------------------------------------------------------------- /+WEB/YouTube.m: -------------------------------------------------------------------------------- 1 | classdef YouTube < WEB.API.Common 2 | % YouTube video service 3 | % https://developers.google.com/youtube/v3 4 | 5 | properties 6 | URL = "https://www.googleapis.com/youtube/v3/" % Base URL 7 | key % API Key 8 | end 9 | 10 | properties (Access = private) 11 | TU = WEB.Utils.Tables 12 | end 13 | 14 | methods 15 | function obj = YouTube(api_key) 16 | %% Constructor 17 | obj.key = api_key; 18 | end 19 | 20 | function [res, err, apiopts] = call_api(obj, method, params, vars) 21 | %% Get via API 22 | [params, apiopts] = obj.prepare_params(params, vars); 23 | req = WEB.API.Req(obj.URL); 24 | req.addurl(method); 25 | req.setquery(params); 26 | req.addquery('key', obj.key); 27 | if isempty(req.getquery('part')) 28 | req.addquery('part', 'snippet'); 29 | end 30 | req.setopts('Timeout', obj.timeout); 31 | [res, err] = get(req); 32 | end 33 | 34 | function [T, res, err] = search(obj, varargin) 35 | %% Get search results 36 | method = 'search'; 37 | params = { 38 | 'q', 'optional', '' 39 | 'channelId', 'optional', '' 40 | 'maxResults', 'optional', 5 41 | 'order', 'optional', 'relevance' 42 | }; 43 | [res, err, apiopts] = obj.call_api(method, params, varargin); 44 | if ~isempty(res) && ~isempty(res.items) 45 | info = obj.TU.concat({res.items.id}); 46 | if obj.TU.isvar(info, 'channelId') 47 | info = removevars(info, 'channelId'); 48 | end 49 | T = [obj.TU.concat({res.items.snippet}) info]; 50 | T.publishedAt = datetime(T.publishedAt, 'InputFormat', "yyyy-MM-dd'T'HH:mm:ss'Z'"); 51 | if ~obj.TU.isvar(T, 'videoId') 52 | T = obj.TU.addfield(T, 'videoId', ''); 53 | T = movevars(T, 'videoId', 'after', 'kind'); 54 | end 55 | if ~obj.TU.isvar(T, 'playlistId') 56 | T = obj.TU.addfield(T, 'playlistId', ''); 57 | end 58 | else 59 | T = table(); 60 | end 61 | end 62 | 63 | function [res, err] = channels_list(obj, part, varargin) 64 | %% Get channels info 65 | method = 'channels'; 66 | params = { 67 | 'part', 'required', part 68 | 'categoryId', 'optional', '' 69 | 'forUsername', 'optional', '' 70 | 'id', 'optional', '' 71 | 'managedByMe', 'optional', false 72 | 'mine', 'optional', false 73 | 'h1', 'optional', '' 74 | 'maxResults', 'optional', 5 75 | 'onBehalfOfContentOwner', 'optional', '' 76 | 'pageToken', 'optional', '' 77 | }; 78 | [res, err, apiopts] = obj.call_api(method, params, varargin); 79 | end 80 | 81 | function [res, err] = playlistItems_list(obj, part, varargin) 82 | %% Get playlist items info 83 | method = 'playlistItems'; 84 | params = { 85 | 'part', 'required', part 86 | 'id', 'optional', '' 87 | 'playlistId', 'optional', '' 88 | 'maxResults', 'optional', 5 89 | 'onBehalfOfContentOwner', 'optional', '' 90 | 'pageToken', 'optional', '' 91 | 'videoId', 'optional', '' 92 | }; 93 | [res, err, apiopts] = obj.call_api(method, params, varargin); 94 | end 95 | 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data/*auth_data* 2 | work 3 | **/*.asv 4 | **/helpsearch-v3 5 | -------------------------------------------------------------------------------- /MATLAB-WEB-API.mltbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/MATLAB-WEB-API.mltbx -------------------------------------------------------------------------------- /MATLAB-WEB-API.prj: -------------------------------------------------------------------------------- 1 | 2 | 3 | MATLAB WEB API 4 | 5 | 6 | 7 | Work with WEB services directly from MATLAB 8 | Work with existed WEB-services API or create your own 9 | ${PROJECT_ROOT}\doc\MATLAB-WEB-API.png 10 | 0.6.3 11 | ${PROJECT_ROOT}\MATLAB WEB API.mltbx 12 | 13 | 14 | 15 | 16 | e1ede9a1-1cd1-42dd-9eac-9fdc38e9c880 17 | % List files contained in your toolbox folder that you would like to exclude 18 | % from packaging. Excludes should be listed relative to the toolbox folder. 19 | % Some examples of how to specify excludes are provided below: 20 | % 21 | % A single file in the toolbox folder: 22 | .git 23 | .github 24 | .gitignore 25 | work 26 | cover.png 27 | **/*.asv 28 | data/*_data* 29 | MATLABWEBAPIDev.m 30 | dev_on.m 31 | install.m 32 | installweb.m 33 | **/helpsearch-v3 34 | true 35 | 36 | 37 | 38 | 39 | ${PROJECT_ROOT}\info.xml 40 | ${PROJECT_ROOT}\doc\GettingStarted.mlx 41 | 42 | 43 | false 44 | 45 | 46 | 47 | R2016a 48 | latest 49 | false 50 | true 51 | true 52 | true 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ${PROJECT_ROOT} 94 | 95 | 96 | ${PROJECT_ROOT}\+WEB 97 | ${PROJECT_ROOT}\.git 98 | ${PROJECT_ROOT}\.gitignore 99 | ${PROJECT_ROOT}\cover.png 100 | ${PROJECT_ROOT}\data 101 | ${PROJECT_ROOT}\dev_on.m 102 | ${PROJECT_ROOT}\doc 103 | ${PROJECT_ROOT}\examples 104 | ${PROJECT_ROOT}\info.xml 105 | ${PROJECT_ROOT}\install.m 106 | ${PROJECT_ROOT}\installweb.m 107 | ${PROJECT_ROOT}\main 108 | ${PROJECT_ROOT}\MATLAB-WEB-API.mltbx 109 | ${PROJECT_ROOT}\MATLAB-WEB-API.prj 110 | ${PROJECT_ROOT}\MATLABWEBAPIDev.m 111 | ${PROJECT_ROOT}\MATLABWEBAPIExtender.m 112 | ${PROJECT_ROOT}\MATLABWEBAPIUpdater.m 113 | ${PROJECT_ROOT}\README.md 114 | ${PROJECT_ROOT}\resources 115 | ${PROJECT_ROOT}\ToolboxConfig.xml 116 | ${PROJECT_ROOT}\work 117 | 118 | 119 | 120 | 121 | 122 | D:\MATLAB\MATLAB-WEB-API\MATLAB WEB API.mltbx 123 | 124 | 125 | 126 | C:\Program Files\MATLAB\R2021a 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | true 138 | 139 | 140 | 141 | 142 | true 143 | 144 | 145 | 146 | 147 | true 148 | 149 | 150 | 151 | 152 | true 153 | 154 | 155 | 156 | 157 | true 158 | 159 | 160 | 161 | 162 | true 163 | 164 | 165 | 166 | 167 | false 168 | false 169 | true 170 | false 171 | false 172 | false 173 | false 174 | false 175 | 10.0 176 | false 177 | true 178 | win64 179 | true 180 | 181 | 182 | -------------------------------------------------------------------------------- /MATLABWEBAPIDev.m: -------------------------------------------------------------------------------- 1 | classdef MATLABWEBAPIDev < handle 2 | % Helps you to build toolbox and deploy it to GitHub 3 | % By Pavel Roslovets, ETMC Exponenta 4 | % https://github.com/ETMC-Exponenta/ToolboxExtender 5 | 6 | properties 7 | ext % Toolbox Extender 8 | vp % project version 9 | end 10 | 11 | methods 12 | function obj = MATLABWEBAPIDev(extender) 13 | % Init 14 | if nargin < 1 15 | obj.ext = MATLABWEBAPIExtender; 16 | else 17 | if ischar(extender) || isStringScalar(extender) 18 | obj.ext = MATLABWEBAPIExtender(extender); 19 | else 20 | obj.ext = extender; 21 | end 22 | end 23 | if ~strcmp(obj.ext.root, pwd) 24 | warning("Project root folder does not math with current folder." +... 25 | newline + "Consider to change folder, delete installed toolbox or restart MATLAB") 26 | end 27 | obj.gvp(); 28 | end 29 | 30 | function vp = gvp(obj) 31 | % Get project version 32 | ppath = obj.ext.getppath(); 33 | if isfile(ppath) 34 | if obj.ext.type == "toolbox" 35 | vp = matlab.addons.toolbox.toolboxVersion(ppath); 36 | else 37 | txt = obj.ext.readtxt(ppath); 38 | vp = char(regexp(txt, '(?<=())(.*?)(?=())', 'match')); 39 | end 40 | else 41 | vp = ''; 42 | end 43 | obj.vp = vp; 44 | end 45 | 46 | function build(obj, vp, gendoc) 47 | % Build toolbox for specified version 48 | ppath = obj.ext.getppath(); 49 | if nargin < 3 || gendoc 50 | obj.gendoc(); 51 | end 52 | if nargin > 1 && ~isempty(vp) 53 | obj.setver(vp); 54 | else 55 | vp = obj.vp; 56 | end 57 | [~, bname] = fileparts(obj.ext.pname); 58 | bpath = fullfile(obj.ext.root, bname); 59 | if obj.ext.type == "toolbox" 60 | obj.updateroot(); 61 | obj.seticons(); 62 | matlab.addons.toolbox.packageToolbox(ppath, bname); 63 | else 64 | matlab.apputil.package(ppath); 65 | movefile(fullfile(obj.ext.root, obj.ext.name + ".mlappinstall"), bpath + ".mlappinstall",'f'); 66 | end 67 | obj.ext.echo("v" + vp + " has been built"); 68 | end 69 | 70 | function test(obj, varargin) 71 | % Build and install 72 | obj.build(varargin{:}); 73 | obj.ext.install(); 74 | end 75 | 76 | function untag(obj, v) 77 | % Delete tag from local and remote 78 | untagcmd1 = sprintf('git push --delete origin v%s', v); 79 | untagcmd2 = sprintf('git tag -d v%s', v); 80 | system(untagcmd1); 81 | system(untagcmd2); 82 | system('git push --tags'); 83 | obj.ext.echo('has been untagged'); 84 | end 85 | 86 | function release(obj, vp) 87 | % Build toolbox, push and tag version 88 | if nargin > 1 89 | obj.vp = vp; 90 | else 91 | vp = ''; 92 | end 93 | if ~isempty(obj.ext.pname) 94 | obj.build(vp, true); 95 | end 96 | obj.push(); 97 | obj.tag(); 98 | obj.ext.echo('has been deployed'); 99 | if ~isempty(obj.ext.pname) 100 | clipboard('copy', ['"' char(obj.ext.getbinpath) '"']) 101 | disp("Binary path was copied to clipboard") 102 | end 103 | disp("* Now create release on GitHub *"); 104 | chapters = ["Summary" "Upgrade Steps" "Breaking Changes"... 105 | "New Features" "Bug Fixes" "Improvements" "Other Changes"]; 106 | chapters = join("# " + chapters, newline); 107 | fprintf("Release notes hint: fill\n%s\n", chapters); 108 | disp("! Don't forget to attach binary from clipboard !"); 109 | pause(1) 110 | web(obj.ext.remote + "/releases/edit/v" + obj.vp, '-browser') 111 | end 112 | 113 | function gendoc(obj, format, docdir, showcred) 114 | % Generate html, pdf or md (beta) from mlx 115 | if nargin < 2 116 | format = "html"; 117 | else 118 | format = string(format); 119 | end 120 | if nargin < 3 121 | docdir = fullfile(obj.ext.root, 'doc'); 122 | end 123 | if nargin < 4 124 | showcred = false; 125 | end 126 | docdir = strip(docdir, '/'); 127 | fs = struct2table(dir(fullfile(docdir, '*.mlx')), 'AsArray', true); 128 | fs = convertvars(fs, 1:3, 'string'); 129 | for i = 1 : height(fs) 130 | [~, fname] = fileparts(fs.name(i)); 131 | fpath = char(fullfile(fs.folder(i), fs.name{i})); 132 | htmlpath = char(fullfile(docdir, fname + "." + format)); 133 | htmlinfo = dir(htmlpath); 134 | convert = isempty(htmlinfo); 135 | if ~convert 136 | fdate = datetime(fs.datenum(i), 'ConvertFrom', 'datenum'); 137 | htmldate = datetime(htmlinfo.datenum, 'ConvertFrom', 'datenum'); 138 | convert = fdate >= htmldate; 139 | end 140 | if convert 141 | fprintf('Converting %s.mlx...\n', fname); 142 | if format == "md" 143 | obj.mlx2md(fpath, htmlpath, showcred); 144 | else 145 | matlab.internal.liveeditor.openAndConvert(fpath, htmlpath); 146 | end 147 | end 148 | end 149 | disp('Docs have been generated'); 150 | end 151 | 152 | function setver(obj, vp) 153 | % Set version 154 | ppath = obj.ext.getppath(); 155 | if obj.ext.type == "toolbox" 156 | matlab.addons.toolbox.toolboxVersion(ppath, vp); 157 | else 158 | txt = obj.ext.readtxt(ppath); 159 | txt = regexprep(txt, '(?<=())(.*?)(?=())', vp); 160 | txt = strrep(txt, '', ''); 161 | obj.ext.writetxt(txt, ppath); 162 | end 163 | obj.gvp(); 164 | end 165 | 166 | function webrel(obj) 167 | % Open GitHub releases webpage 168 | obj.ext.webrel(); 169 | end 170 | 171 | function check(obj) 172 | % Check deployed release 173 | upd = MATLABWEBAPIUpdater(obj.ext); 174 | upd.fetch(); 175 | disp("Latest release: v" + upd.vr); 176 | if isempty(upd.rel) 177 | fprintf('[X] Release notes\n'); 178 | end 179 | if isempty(upd.relsum) 180 | fprintf('[X] Release notes summary\n'); 181 | end 182 | if isempty(upd.bin) 183 | fprintf('[X] Toolbox binary\n'); 184 | end 185 | end 186 | 187 | function [com, url] = webinstaller(obj) 188 | % Get web installer command 189 | url = obj.shorturl(obj.ext.getrawurl('installweb.m')); 190 | com = sprintf("eval(webread('%s'))", url); 191 | end 192 | 193 | end 194 | 195 | 196 | methods (Hidden) 197 | 198 | function updateroot(obj) 199 | % Update project root 200 | service = com.mathworks.toolbox_packaging.services.ToolboxPackagingService; 201 | pr = service.openProject(obj.ext.getppath()); 202 | service.removeToolboxRoot(pr); 203 | service.setToolboxRoot(pr, obj.ext.root); 204 | service.closeProject(pr); 205 | end 206 | 207 | function seticons(obj) 208 | % Set icons of app in toolbox 209 | xmlfile = 'DesktopToolset.xml'; 210 | oldtxt = ''; 211 | newtxt = ''; 212 | if isfile(xmlfile) && isfolder('resources') 213 | if all(isfile("resources/icon_" + [16 24] + ".png")) 214 | txt = obj.ext.readtxt(xmlfile); 215 | if contains(txt, oldtxt) 216 | txt = replace(txt, oldtxt, newtxt); 217 | obj.ext.writetxt(txt, xmlfile); 218 | end 219 | end 220 | end 221 | end 222 | 223 | function push(obj) 224 | % Commit and push project to GitHub 225 | commitcmd = sprintf('git commit -m v%s', obj.vp); 226 | system('git add .'); 227 | system(commitcmd); 228 | system('git push'); 229 | obj.ext.echo('has been pushed'); 230 | end 231 | 232 | function tag(obj, vp) 233 | % Tag git project and push tag 234 | if nargin < 2 235 | vp = obj.vp; 236 | end 237 | tagcmd = sprintf('git tag -a v%s -m v%s', vp, vp); 238 | system(tagcmd); 239 | system('git push --tags'); 240 | obj.ext.echo('has been tagged'); 241 | end 242 | 243 | function url = shorturl(obj, url) 244 | % Shorten URL by git.io 245 | host = "https://git.io/"; 246 | url = webwrite(host + "create", 'url', url); 247 | url = host + url; 248 | end 249 | 250 | end 251 | 252 | 253 | methods (Hidden = true) 254 | 255 | function mlx2md(obj, fpath, htmlpath, showcred) 256 | % Convert mlx-script to markdown md-file (beta) 257 | if nargin < 4 258 | showcred = false; 259 | end 260 | [~, fname] = fileparts(fpath); 261 | tempf = "_temp_" + fname + ".m"; 262 | matlab.internal.liveeditor.openAndConvert(fpath, char(tempf)); 263 | txt = string(split(fileread(tempf), newline)); 264 | delete(tempf); 265 | txt = erase(txt, char(13)); 266 | % Convert code 267 | code = find(~startsWith(txt, '%') & txt ~= ""); 268 | txt2 = strings(); 269 | for i = 1 : length(txt) 270 | if ismember(i, code) 271 | if ~ismember(i-1, code) 272 | txt2 = txt2 + "``` MATLAB" + newline; 273 | end 274 | txt2 = txt2 + txt(i) + newline; 275 | if ~ismember(i+1, code) 276 | txt2 = txt2 + "```" + newline; 277 | end 278 | else 279 | txt2 = txt2 + txt(i) + newline; 280 | end 281 | end 282 | txt = string(split(txt2, newline)); 283 | % Convert first title 284 | if startsWith(txt(1), '%% ') 285 | txt(1) = replace(txt(1), '%% ', '# '); 286 | end 287 | % Convert other titles 288 | titles = startsWith(txt, '%% ') & txt ~= "%% "; 289 | txt(titles) = replace(txt(titles), '%% ', '## '); 290 | % Convert lists 291 | lists = find(startsWith(txt, '% * ')); 292 | txt(lists) = extractAfter(txt(lists), '% '); 293 | lists = find(startsWith(txt, '% # ')); 294 | txt(lists) = replace(txt(lists), '% # ', '* '); 295 | % Convert text 296 | text = find(startsWith(txt, '% ')); 297 | txt2 = strings(); 298 | for i = 1 : length(txt) 299 | if ismember(i, text) 300 | str = char(txt(i)); 301 | str = replace(str, '|', '`'); 302 | %str = replace(str, '*', '**'); 303 | txt2 = txt2 + str(3:end); 304 | if ~ismember(i+1, text) 305 | txt2 = txt2 + newline; 306 | end 307 | else 308 | txt2 = txt2 + txt(i) + newline; 309 | end 310 | end 311 | txt = string(split(txt2, newline)); 312 | br = txt == "%% "; 313 | txt(br) = ""; 314 | % Convert links 315 | links = extractBetween(join(txt, newline), '<', '>'); 316 | urls = extractBefore(links, ' '); 317 | names = extractAfter(links, ' '); 318 | txt = replace(txt, "<" + links + ">", "[" + names + "](" + urls + ")"); 319 | if showcred 320 | txt(end+1) = sprintf("***\n*Generated from %s.mlx with [Toolbox Extender](%s)*",... 321 | fname, 'https://github.com/ETMC-Exponenta/ToolboxExtender'); 322 | end 323 | obj.ext.writetxt(join(txt, newline), htmlpath, 'utf-8'); 324 | end 325 | 326 | end 327 | 328 | 329 | methods (Static) 330 | 331 | function exclude(ppath, fmask) 332 | if isfile(ppath) 333 | try 334 | % Exclude file or folder from Toolbox Project 335 | service = com.mathworks.toolbox_packaging.services.ToolboxPackagingService; 336 | pr = service.openProject(ppath); 337 | ex = service.getExcludeFilter(pr); 338 | ex = split(string(ex), newline); 339 | fmask = string(fmask); 340 | for i = 1 : length(fmask) 341 | if ~ismember(fmask(i), ex) 342 | ex = [ex; fmask(i)]; 343 | service.setExcludeFilter(pr, join(ex, newline)); 344 | end 345 | end 346 | service.closeProject(pr); 347 | catch 348 | end 349 | end 350 | end 351 | 352 | end 353 | 354 | 355 | end -------------------------------------------------------------------------------- /MATLABWEBAPIExtender.m: -------------------------------------------------------------------------------- 1 | classdef MATLABWEBAPIExtender < handle 2 | % Contains core functions. Required for other classes and functionality 3 | % By Pavel Roslovets, ETMC Exponenta 4 | % https://github.com/ETMC-Exponenta/ToolboxExtender 5 | 6 | properties 7 | name % project name 8 | pname % name of project file 9 | type % type of project 10 | root % root dir 11 | remote % GitHub link 12 | vc % current installed version 13 | extv % Toolbox Extender version 14 | end 15 | 16 | properties (Hidden) 17 | config = 'ToolboxConfig.xml' % configuration file name 18 | project % MATLAB Project handle 19 | end 20 | 21 | methods 22 | function obj = MATLABWEBAPIExtender(root) 23 | % Init 24 | if nargin < 1 25 | obj.root = fileparts(mfilename('fullpath')); 26 | else 27 | obj.root = root; 28 | end 29 | end 30 | 31 | function set.root(obj, root) 32 | % Set root path 33 | obj.root = root; 34 | if ~obj.readconfig() 35 | obj.getpname(); 36 | obj.gettype(); 37 | obj.getname(); 38 | obj.getremote(); 39 | end 40 | obj.gvc(); 41 | end 42 | 43 | function [vc, guid] = gvc(obj) 44 | % Get current installed version 45 | switch obj.type 46 | case "toolbox" 47 | tbx = matlab.addons.toolbox.installedToolboxes; 48 | if ~isempty(tbx) 49 | tbx = struct2table(tbx, 'AsArray', true); 50 | idx = strcmp(tbx.Name, obj.name); 51 | vcs = string(tbx.Version(idx)); 52 | guid = tbx.Guid(idx); 53 | vc = ''; 54 | for i = 1 : length(vcs) 55 | if matlab.addons.isAddonEnabled(guid{i}, vcs(i)) 56 | vc = char(vcs(i)); 57 | break 58 | end 59 | end 60 | else 61 | vc = ''; 62 | guid = ''; 63 | end 64 | case "app" 65 | apps = matlab.apputil.getInstalledAppInfo; 66 | vc = ''; 67 | guid = ''; 68 | otherwise 69 | vc = ''; 70 | end 71 | obj.vc = vc; 72 | end 73 | 74 | function res = install(obj, fpath) 75 | % Install toolbox or app 76 | if nargin < 2 77 | fpath = obj.getbinpath(); 78 | end 79 | switch obj.type 80 | case "toolbox" 81 | res = matlab.addons.install(fpath); 82 | case "app" 83 | res = matlab.apputil.install(fpath); 84 | otherwise 85 | error('Unsupported for %s\n', obj.type); 86 | end 87 | obj.gvc(); 88 | obj.echo('has been installed'); 89 | end 90 | 91 | function uninstall(obj) 92 | % Uninstall toolbox or app 93 | [~, guid] = obj.gvc(); 94 | if isempty(guid) 95 | disp('Nothing to uninstall'); 96 | else 97 | guid = string(guid); 98 | for i = 1 : length(guid) 99 | switch obj.type 100 | case "toolbox" 101 | matlab.addons.uninstall(char(guid(i))); 102 | case "app" 103 | matlab.apputil.uninstall(char(guid(i))); 104 | otherwise 105 | error('Unsupported for %s\n', obj.type); 106 | end 107 | end 108 | disp(obj.name + " was uninstalled"); 109 | try 110 | obj.gvc(); 111 | end 112 | end 113 | end 114 | 115 | function doc(obj, name) 116 | % Open page from documentation 117 | docdir = fullfile(obj.root, 'doc'); 118 | if isfolder(docdir) 119 | if (nargin < 2) || isempty(name) 120 | name = 'GettingStarted'; 121 | end 122 | if ~any(endsWith(name, {'.mlx' '.html'})) 123 | if computer == "GLNXA64" %% Linux and MATLAB Online 124 | name = name + ".mlx"; 125 | else 126 | name = name + ".html"; 127 | end 128 | end 129 | docpath = fullfile(docdir, name); 130 | if endsWith(name, '.html') 131 | web(char(docpath)); 132 | else 133 | open(char(docpath)); 134 | end 135 | end 136 | end 137 | 138 | function examples(obj) 139 | % cd to Examples dir 140 | expath = fullfile(obj.root, 'examples'); 141 | cd(expath); 142 | end 143 | 144 | function web(obj) 145 | % Open GitHub page 146 | web(obj.remote, '-browser'); 147 | end 148 | 149 | function addfav(obj, label, code, icon) 150 | % Add favorite 151 | favs = com.mathworks.mlwidgets.favoritecommands.FavoriteCommands.getInstance(); 152 | nfav = com.mathworks.mlwidgets.favoritecommands.FavoriteCommandProperties(); 153 | nfav.setLabel(label); 154 | nfav.setCategoryLabel(obj.name); 155 | nfav.setCode(code); 156 | if nargin > 3 157 | [ipath, iname, iext] = fileparts(icon); 158 | nfav.setIconPath(fullfile(obj.root, ipath)); 159 | nfav.setIconName(iname + string(iext)); 160 | end 161 | nfav.setIsOnQuickToolBar(true); 162 | favs.addCommand(nfav); 163 | end 164 | 165 | function yes = isfav(obj, label) 166 | % Does favorite exist 167 | favs = com.mathworks.mlwidgets.favoritecommands.FavoriteCommands.getInstance(); 168 | yes = favs.hasCommand(label, obj.name); 169 | end 170 | 171 | function yes = isfavs(obj) 172 | % Does favorites category exist 173 | favs = com.mathworks.mlwidgets.favoritecommands.FavoriteCommands.getInstance(); 174 | yes = favs.hasCategory(obj.name); 175 | end 176 | 177 | function yes = rmfav(obj, label) 178 | % Remove favorite 179 | favs = com.mathworks.mlwidgets.favoritecommands.FavoriteCommands.getInstance(); 180 | yes = favs.removeCommand(label, obj.name); 181 | end 182 | 183 | function rmfavs(obj) 184 | % Remove all favorites 185 | favs = com.mathworks.mlwidgets.favoritecommands.FavoriteCommands.getInstance(); 186 | favs.removeCategory(obj.name) 187 | end 188 | 189 | end 190 | 191 | 192 | methods (Hidden) 193 | 194 | function echo(obj, msg) 195 | % Display service message 196 | fprintf('%s %s\n', obj.name, msg); 197 | end 198 | 199 | function name = getname(obj) 200 | % Get project name from project file 201 | name = ''; 202 | ppath = obj.getppath(); 203 | if isfile(ppath) 204 | switch obj.type 205 | case "toolbox" 206 | txt = obj.readtxt(ppath); 207 | name = char(extractBetween(txt, '', '')); 208 | case "project" 209 | name = obj.project.Name; 210 | end 211 | end 212 | obj.name = name; 213 | end 214 | 215 | function pname = getpname(obj) 216 | % Get project file name 217 | fs = dir(fullfile(obj.root, '*.prj')); 218 | if ~isempty(fs) 219 | names = {fs.name}; 220 | isproj = false(1, length(names)); 221 | for i = 1 : length(names) 222 | txt = obj.readtxt(fullfile(obj.root, names{i})); 223 | isproj(i) = ~contains(txt, ' 1 292 | name = char(name + string(cname)); 293 | end 294 | name = matlab.lang.makeValidName(name); 295 | end 296 | 297 | function txt = readtxt(~, fpath) 298 | % Read text from file 299 | if isfile(fpath) 300 | f = fopen(fpath, 'r', 'n', 'windows-1251'); 301 | txt = fread(f, '*char')'; 302 | fclose(f); 303 | else 304 | txt = ''; 305 | end 306 | end 307 | 308 | function writetxt(~, txt, fpath, encoding) 309 | % Wtite text to file 310 | if nargin < 4 311 | encoding = 'windows-1251'; 312 | end 313 | fid = fopen(fpath, 'w', 'n', encoding); 314 | fwrite(fid, unicode2native(txt, encoding)); 315 | fclose(fid); 316 | end 317 | 318 | function txt = txtrep(obj, fpath, old, new) 319 | % Replace in txt file 320 | txt = obj.readtxt(fpath); 321 | txt = replace(txt, old, new); 322 | obj.writetxt(txt, fpath); 323 | end 324 | 325 | function [bpath, bname] = getbinpath(obj) 326 | % Get generated binary file path 327 | [~, name] = fileparts(obj.pname); 328 | switch obj.type 329 | case "toolbox" 330 | ext = ".mltbx"; 331 | case "app" 332 | ext = ".mlappinstall"; 333 | otherwise 334 | error('Unsupported for %s\n', obj.type); 335 | end 336 | bname = name + ext; 337 | bpath = fullfile(obj.root, bname); 338 | end 339 | 340 | function ok = readconfig(obj) 341 | % Read config from xml file 342 | confpath = fullfile(obj.root, obj.config); 343 | ok = isfile(confpath); 344 | if ok 345 | xml = xmlread(confpath); 346 | conf = obj.getxmlitem(xml, 'config', 0); 347 | obj.name = obj.getxmlitem(conf, 'name'); 348 | obj.pname = obj.getxmlitem(conf, 'pname'); 349 | obj.type = obj.getxmlitem(conf, 'type'); 350 | obj.remote = obj.cleargit(obj.getxmlitem(conf, 'remote')); 351 | obj.extv = obj.getxmlitem(conf, 'extv'); 352 | end 353 | end 354 | 355 | function i = getxmlitem(~, xml, name, getData) 356 | % Get item from XML 357 | if nargin < 4 358 | getData = true; 359 | end 360 | i = xml.getElementsByTagName(name); 361 | i = i.item(0); 362 | if getData 363 | i = i.getFirstChild; 364 | if ~isempty(i) 365 | i = i.getData; 366 | end 367 | i = char(i); 368 | end 369 | end 370 | 371 | function [nname, npath] = cloneclass(obj, classname, sourcedir, prename) 372 | % Clone Toolbox Extander class to current Project folder 373 | if nargin < 4 374 | prename = "Toolbox"; 375 | end 376 | if nargin < 3 377 | sourcedir = pwd; 378 | end 379 | if nargin < 2 380 | classname = "Extender"; 381 | else 382 | classname = lower(char(classname)); 383 | classname(1) = upper(classname(1)); 384 | end 385 | pname = obj.getvalidname; 386 | if isempty(pname) 387 | pname = 'Toolbox'; 388 | end 389 | oname = string(prename) + classname; 390 | nname = pname + string(classname); 391 | npath = fullfile(obj.root, nname + ".m"); 392 | opath = fullfile(sourcedir, oname + ".m"); 393 | copyfile(opath, npath); 394 | obj.txtrep(npath, "obj = " + oname, "obj = " + nname); 395 | obj.txtrep(npath, "classdef " + oname, "classdef " + nname); 396 | obj.txtrep(npath, "obj.ext = MATLABWEBAPIExtender", "obj.ext = " + obj.getvalidname + "Extender"); 397 | obj.txtrep(npath, "upd = MATLABWEBAPIUpdater", "upd = " + obj.getvalidname + "Updater"); 398 | end 399 | 400 | function name = getselfname(~) 401 | % Get self class name 402 | name = mfilename('class'); 403 | end 404 | 405 | function webrel(obj) 406 | % Open GitHub releases webpage 407 | web(obj.remote + "/releases", '-browser'); 408 | end 409 | 410 | function repo = getrepo(obj) 411 | % Get repo string from remote URL 412 | repo = extractAfter(obj.remote, 'https://github.com/'); 413 | end 414 | 415 | function url = getlatesturl(obj) 416 | % Get latest release URL 417 | url = obj.getapiurl() + "/releases/latest"; 418 | end 419 | 420 | function url = getapiurl(obj) 421 | % Get GitHub API URL 422 | url = "https://api.github.com/repos/" + obj.getrepo(); 423 | end 424 | 425 | function url = getrawurl(obj, fname) 426 | % Get GitHub raw source URL 427 | url = sprintf("https://raw.githubusercontent.com/%s/master/%s", obj.getrepo(), fname); 428 | end 429 | 430 | end 431 | 432 | methods (Hidden, Static) 433 | 434 | function remote = cleargit(remote) 435 | % Delete .git 436 | remote = char(remote); 437 | if endsWith(remote, '.git') 438 | remote = remote(1:end-4); 439 | end 440 | end 441 | 442 | end 443 | end -------------------------------------------------------------------------------- /MATLABWEBAPIUpdater.m: -------------------------------------------------------------------------------- 1 | classdef MATLABWEBAPIUpdater < handle 2 | % Control version of installed toolbox and update it from GitHub 3 | % By Pavel Roslovets, ETMC Exponenta 4 | % https://github.com/ETMC-Exponenta/ToolboxExtender 5 | 6 | properties 7 | ext % Toolbox Extender 8 | vr % latest remote version form internet (GitHub) 9 | isupd % update is available 10 | relsum % release notes summary 11 | rel % release notes 12 | bin % Toolbox binary 13 | end 14 | 15 | properties (Hidden) 16 | res % GitHub resources 17 | end 18 | 19 | methods 20 | function obj = MATLABWEBAPIUpdater(extender) 21 | %% Constructor 22 | if nargin < 1 23 | obj.ext = MATLABWEBAPIExtender; 24 | else 25 | obj.ext = extender; 26 | end 27 | end 28 | 29 | function [res, err] = fetch(obj) 30 | %% Fetch resources from GitHub 31 | url = obj.ext.getlatesturl(); 32 | res = ''; 33 | try 34 | res = webread(url); 35 | err = []; 36 | obj.res = res; 37 | obj.vr = erase(res.tag_name, 'v'); 38 | obj.rel = res.body; 39 | % Extract release summary 40 | sum = ''; 41 | if contains(obj.rel, '# Summary') 42 | sum = extractAfter(obj.rel, '# Summary'); 43 | if contains(sum, '#') 44 | sum = extractBefore(sum, '#'); 45 | end 46 | end 47 | sum = char(strtrim(sum)); 48 | obj.relsum = sum; 49 | % Extract update is available 50 | obj.isupd = ~isempty(obj.vr) & ~isequal(obj.ext.vc, obj.vr); 51 | % Get binary information 52 | assets = struct2table(obj.res.assets, 'AsArray', 1); 53 | assets = assets(endsWith(assets.name, '.mltbx'), :); 54 | if ~isempty(assets) 55 | obj.bin = table2struct(assets(1, :)); 56 | else 57 | obj.bin = []; 58 | end 59 | catch err 60 | end 61 | end 62 | 63 | function vr = gvr(obj) 64 | %% Get remote version from GitHub 65 | if isempty(obj.vr) 66 | obj.fetch(); 67 | end 68 | vr = obj.vr; 69 | end 70 | 71 | function rel = getrel(obj) 72 | %% Get release notes 73 | if isempty(obj.res) 74 | obj.fetch(); 75 | end 76 | rel = obj.rel; 77 | end 78 | 79 | function sum = getrelsum(obj) 80 | %% Get release notes summary 81 | if isempty(obj.res) 82 | obj.fetch(); 83 | end 84 | sum = obj.relsum; 85 | end 86 | 87 | function webrel(obj) 88 | %% Open GitHub releases webpage 89 | obj.ext.webrel(); 90 | end 91 | 92 | function [vc, vr] = ver(obj) 93 | %% Check curent installed and remote versions 94 | vc = obj.ext.gvc(); 95 | if nargout == 0 96 | if isempty(vc) 97 | fprintf('%s is not installed\n', obj.ext.name); 98 | else 99 | fprintf('Installed version: %s\n', vc); 100 | end 101 | end 102 | % Check remote version 103 | vr = obj.gvr(); 104 | if nargout == 0 105 | if ~isempty(vr) 106 | fprintf('Latest version: %s\n', vr); 107 | if isequal(vc, vr) 108 | fprintf('You use the latest version\n'); 109 | else 110 | fprintf('* Update is available: %s->%s *\n', vc, vr); 111 | fprintf("To update call 'update' method of " + mfilename + "\n"); 112 | end 113 | else 114 | fprintf('No remote version is available\n'); 115 | end 116 | end 117 | end 118 | 119 | function yes = isonline(~) 120 | %% Check connection to internet is available 121 | try 122 | java.net.InetAddress.getByName('google.com'); 123 | yes = true; 124 | catch 125 | yes = false; 126 | end 127 | end 128 | 129 | function isupd = isupdate(obj, cbfun, delay) 130 | %% Check that update is available 131 | if obj.isonline() 132 | if nargin < 2 133 | obj.fetch(); 134 | isupd = obj.isupd; 135 | else 136 | if nargin < 3 137 | delay = 1; 138 | end 139 | isupd = false; 140 | obj.run_task(@(~, ~) obj.isupd_async(cbfun), delay); 141 | end 142 | else 143 | isupd = false; 144 | end 145 | end 146 | 147 | function installweb(obj, dpath) 148 | %% Download and install the latest version from remote (GitHub) 149 | if nargin < 2 150 | dpath = tempname; 151 | mkdir(dpath); 152 | end 153 | if isempty(obj.res) 154 | obj.gvr(); 155 | end 156 | if ~isempty(obj.vr) 157 | fprintf('* Installation of %s is started *\n', obj.ext.name); 158 | fprintf('Installing the latest version: v%s...\n', obj.vr); 159 | if isempty(obj.bin) 160 | error('No toolbox file were found on GitHub. Contact toolbox author'); 161 | end 162 | fpath = fullfile(dpath, obj.bin.name); 163 | websave(fpath, obj.bin.browser_download_url); 164 | r = obj.ext.install(fpath); 165 | fprintf('%s v%s has been installed\n', r.Name{1}, r.Version{1}); 166 | delete(fpath); 167 | end 168 | end 169 | 170 | function update(obj, delay, cbpre, varargin) 171 | %% Update installed version to the latest from remote (GitHub) 172 | if obj.isupdate() 173 | if nargin < 2 174 | delay = 1; 175 | end 176 | dpath = tempname; 177 | mkdir(dpath); 178 | TE = feval(obj.ext.getselfname()); 179 | TE.root = dpath; 180 | TE.name = 'Temp'; 181 | vname = obj.ext.getvalidname(); 182 | if vname == "ToolboxExtender" 183 | vname = "Toolbox"; 184 | end 185 | TE.cloneclass('Extender', obj.ext.root, vname); 186 | cname = TE.cloneclass('Updater', obj.ext.root, vname); 187 | copyfile(fullfile(obj.ext.root, obj.ext.config), dpath); 188 | if nargin > 2 && ~isempty(cbpre) 189 | cbpre(); 190 | end 191 | taskfcn = @(~, ~) obj.installweb_async(dpath, cname, varargin{:}); 192 | obj.run_task(taskfcn, delay); 193 | end 194 | end 195 | 196 | end 197 | 198 | methods (Hidden) 199 | 200 | function isupd_async(obj, cbfun) 201 | %% Task for async ver timer 202 | obj.fetch(); 203 | cbfun(obj.isupd); 204 | end 205 | 206 | function installweb_async(~, dpath, cname, cbpost) 207 | %% Task for update timer 208 | p0 = cd(dpath); 209 | TU = eval(cname); 210 | TU.installweb(dpath); 211 | cd(p0); 212 | if nargin > 3 213 | cbpost(); 214 | end 215 | end 216 | 217 | function run_task(obj, fcn, delay) 218 | %% Run delayed asynchronous task 219 | tmr = timer('ExecutionMode', 'singleShot', 'StartDelay', delay,... 220 | 'TimerFcn', fcn, 'StopFcn', @(tmr,~,~) delete(tmr),... 221 | 'Name', obj.ext.name + " task"); 222 | start(tmr); 223 | end 224 | 225 | end 226 | 227 | end 228 | 229 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MATLAB WEB API 2 | ![MATLAB WEB API Cover](/cover.png) 3 | 4 | [English](#description-gb) | [Русский](#Описание-ru) 5 | 6 | ## Description :gb: 7 | 8 | Framework for building handy WEB APIs to work with any WEB services from MATLAB 9 | 10 | [![View MATLAB WEB API (connect to WEB services using OAuth 1.0/2.0) on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/68611) 11 | 12 | #### Key features: 13 | 14 | * Set of methods, templates and examples for quick creation of WEB API to work with any WEB service from MATLAB 15 | * Library to work with WEB requests 16 | * Support of OAuth 1.0, OAuth 2.0 access protocols 17 | #### At the moment, the work with services is partially done: 18 | * [Bing Maps](https://www.bing.com/maps) - Mapping service 19 | * [Data.gov.ru](http://data.gov.ru/) - Open data of Russian Federation 20 | * [Flickr](http://flickr.com/) - Photohosting ![OAuth logo](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Oauth_logo.svg/16px-Oauth_logo.svg.png "OAuth 1.0") 21 | * [HeadHunter](http://hh.com/) - Russian recruiting service 22 | * [ip-api.com](http://ip-api.com) - IP geolocation 23 | * [NetSuite](http://www.netsuite.com/portal/home.shtml) - CRM system ![OAuth logo](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Oauth_logo.svg/16px-Oauth_logo.svg.png "OAuth 1.0") 24 | * [OpenWeatherMap](https://openweathermap.org/) - Weather service 25 | * [REST Countries](http://restcountries.eu) - Countries information 26 | * [uinames.com](https://uinames.com/) - Random names generator 27 | * [VK](https://vk.com/) - Russian social network ![OAuth 2.0 logo](https://cdn-images-1.medium.com/max/16/0*QWNG5EAnPSaUSAHH.png "OAuth 2.0") 28 | * [YouTube](https://youtube.com/) - Video hosting 29 | * [MPS](https://www.mathworks.com/products/matlab-production-server.html) - MATLAB Production Server 30 | * [TelegramBot](https://core.telegram.org/bots/api) - Telegram Bot API 31 | 32 | **[Welcome aboard!](https://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project) Together we will add more of services API and improve the existing.** 33 | 34 | ## How to install 35 | 36 | ### For use only 37 | 38 | #### 1st approach (install from scratch) 39 | 40 | In MATLAB execute: 41 | 42 | ```matlab 43 | eval(webread('https://exponenta.ru/install/web')) 44 | ``` 45 | #### 2nd approach (install from scratch) 46 | 47 | * Download [MATLAB-WEB-API.mltbx](https://roslovets.github.io/ghbin#ETMC-Exponenta/MATLAB-WEB-API#MATLAB-WEB-API.mltbx) 48 | * Open it 49 | 50 | #### 3rd approach (update installed) 51 | 52 | Check the current and latest versions: 53 | ```matlab 54 | WEB.API.Ver 55 | ``` 56 | Update to the latest version: 57 | ```matlab 58 | WEB.API.Update 59 | ``` 60 | 61 | ### For development 62 | 63 | * Install [Git](https://git-scm.com/downloads) 64 | * [Learn](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics) how to use Git 65 | * In OS command line execute: 66 | ```bash 67 | git clone https://github.com/ETMC-Exponenta/MATLAB-WEB-API.git 68 | ``` 69 | 70 | ## Where to start 71 | 72 | Start with [examples](/examples): 73 | 74 | ```matlab 75 | WEB.API.Examples 76 | ``` 77 | *Note: to work with some WEB Services you need to register and get access keys. See particular Service Developer documentation* 78 | 79 | Examine documentation: 80 | 81 | ```matlab 82 | WEB.API.Doc 83 | ``` 84 | 85 | --- 86 | ## Описание :ru: 87 | 88 | Фреймворк для создания удобных WEB API для работы с любыми WEB-сервисами из MATLAB 89 | 90 | [Страница проекта на MathWorks File Exchange](https://www.mathworks.com/matlabcentral/fileexchange/68611) 91 | 92 | [Статья с описанием в сообществе Экспонента](https://hub.exponenta.ru/post/matlab-web-api-dlya-raboty-s-veb-servisami898) 93 | 94 | #### Ключевые особенности: 95 | 96 | * Набор методов, шаблонов и примеров для быстрого создания WEB API для работы с любым WEB-сервисом из MATLAB 97 | * Библиотека для работы с WEB-запросами 98 | * Поддержка протоколов авторизации OAuth 1.0, OAuth 2.0 99 | #### На данный момент частично реализована работа с сервисами: 100 | * [Bing Maps](https://www.bing.com/maps) - картографический сервис 101 | * [Data.gov.ru](http://data.gov.ru/) - открытые данные России 102 | * [Flickr](http://flickr.com/) - фотохостинг ![OAuth logo](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Oauth_logo.svg/16px-Oauth_logo.svg.png "OAuth 1.0") 103 | * [HeadHunter](http://hh.com/) - сервис поиска работы 104 | * [ip-api.com](http://ip-api.com) - геолокация оп IP 105 | * [NetSuite](http://www.netsuite.com/portal/home.shtml) - CRM-система ![OAuth logo](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d2/Oauth_logo.svg/16px-Oauth_logo.svg.png "OAuth 1.0") 106 | * [OpenWeatherMap](https://openweathermap.org/) - сервис погоды 107 | * [REST Countries](http://restcountries.eu) - информация о странах 108 | * [uinames.com](https://uinames.com/) - генератор случайных имён 109 | * [VK](https://vk.com/) - российская социальная сеть ![OAuth 2.0 logo](https://cdn-images-1.medium.com/max/16/0*QWNG5EAnPSaUSAHH.png "OAuth 2.0") 110 | * [YouTube](https://youtube.com/) - видео-хостинг 111 | * [MPS](https://www.mathworks.com/products/matlab-production-server.html) - MATLAB Production Server 112 | * [TelegramBot](https://core.telegram.org/bots/api) - Telegram Bot API 113 | 114 | **[Присоединяйтесь!](https://git-scm.com/book/ru/v2/GitHub-Внесение-собственного-вклада-в-проекты) Вместе мы добавим больше сервисов и улучшим работу с имеющимися.** 115 | 116 | ## Как установить 117 | 118 | ### Только для использования 119 | 120 | #### Способ 1 (установка с нуля) 121 | 122 | В MATLAB выполните: 123 | 124 | ```matlab 125 | eval(webread('https://exponenta.ru/install/web')) 126 | ``` 127 | #### Способ 2 (установка с нуля) 128 | 129 | * Скачайте [MATLAB-WEB-API.mltbx](https://roslovets.github.io/ghbin#ETMC-Exponenta/MATLAB-WEB-API#MATLAB-WEB-API.mltbx) 130 | * Откройте его 131 | 132 | #### Способ 3 (обновление) 133 | 134 | Узнать текущую и последнюю версии: 135 | ```matlab 136 | WEB.API.Ver 137 | ``` 138 | Обновление до последней версии: 139 | ```matlab 140 | WEB.API.Update 141 | ``` 142 | 143 | ### Для разработчиков 144 | 145 | * Установите [Git](https://git-scm.com/downloads) 146 | * [Изучите](https://git-scm.com/book/ru/v2/%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B-Git) основы работы с Git 147 | * В командной строке ОС выполните: 148 | ```bash 149 | git clone https://github.com/ETMC-Exponenta/MATLAB-WEB-API.git 150 | ``` 151 | 152 | ## С чего начать 153 | 154 | Начните с изучения [примеров](/examples): 155 | ```matlab 156 | WEB.API.Examples 157 | ``` 158 | 159 | *Обратите внимание: для работы с некоторыми сервисами вам потребуется зарегистрироваться и получить ключи доступа. Изучайте документацию для разработчиков соответствующих сервисов* 160 | 161 | Изучите документацию: 162 | 163 | ```matlab 164 | WEB.API.Doc 165 | ``` 166 | -------------------------------------------------------------------------------- /ToolboxConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | MATLAB WEB API 4 | MATLAB-WEB-API.prj 5 | toolbox 6 | https://github.com/ETMC-Exponenta/MATLAB-WEB-API 7 | 2.1.0 8 | 9 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/cover.png -------------------------------------------------------------------------------- /data/README.md: -------------------------------------------------------------------------------- 1 | :gb: Folder for any project data (i.e. authentication data, images) 2 | 3 | :ru: Папка для хранения данных проекта (ключей авторизации, изображений и т.д.) 4 | -------------------------------------------------------------------------------- /dev_on.m: -------------------------------------------------------------------------------- 1 | % Generated with Toolbox Extender https://github.com/ETMC-Exponenta/ToolboxExtender 2 | clear all 3 | dev = MATLABWEBAPIDev 4 | fprintf(' Dev: %s v%s (%s)\n\n', dev.ext.name, dev.vp, dev.ext.root) 5 | % to deploy run: dev.deploy(v) i.e. dev.deploy('0.1.1') 6 | % to build run dev.build or dev.build(v) 7 | % to push new version to GitHub: dev.push 8 | % to tag new release: dev.tag -------------------------------------------------------------------------------- /doc/Examples.html: -------------------------------------------------------------------------------- 1 | 2 | Examples

Examples

Package contains examples for all APIs. Go to examples:
WEB.API.Examples
6 |
7 | 15 |
-------------------------------------------------------------------------------- /doc/Examples.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/Examples.mlx -------------------------------------------------------------------------------- /doc/GettingStarted.html: -------------------------------------------------------------------------------- 1 | 2 | MATLAB WEB API

MATLAB WEB API

Description

Framework for building handy WEB APIs to work with any WEB services from MATLAB.

Features

  • Set of methods, templates and examples for quick creation of WEB API to work with any WEB service from MATLAB
  • Library to work with WEB requests
  • Support of OAuth 1.0, OAuth 2.0 access protocols

WEB APIs

At the moment, the work with services is partially done:

How to install

* For use only *

1st approach (install from scratch)
eval(webread('https://exponenta.ru/install/web'))
2nd approach (install from scratch)
3rd approach (update installed)
Check the current and latest versions:
WEB.API.Ver
Update to the latest version:
WEB.API.Update

* For development *

  • Install Git
  • Learn how to use Git
  • In OS command line execute:
git clone https://github.com/ETMC-Exponenta/MATLAB-WEB-API.git

Where to start

Start with examples:
WEB.API.Examples
Note: to work with some WEB Services you need to register and get access keys. See particular Service Developer documentation
Examine documentation:
WEB.API.Doc

Join us

Together we will add more of services API and improve the existing!
11 |
12 | 92 |
-------------------------------------------------------------------------------- /doc/GettingStarted.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/GettingStarted.mlx -------------------------------------------------------------------------------- /doc/MATLAB-WEB-API.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/MATLAB-WEB-API.png -------------------------------------------------------------------------------- /doc/Reference.html: -------------------------------------------------------------------------------- 1 | 2 | Reference

Reference

  • WEB.API - core WEB APIs components (WEB requests processing, authentication, API building)
  • WEB.Utils - additional tools (data processing, visualization, etc.)

Realized APIs

(Rediness: 🌑 🌘 🌗 🌖 🌕)
7 |
8 | 45 |
-------------------------------------------------------------------------------- /doc/Reference.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/Reference.mlx -------------------------------------------------------------------------------- /doc/WebApi.html: -------------------------------------------------------------------------------- 1 | 2 | WEB.API

WEB.API

Key components for any WEB API

WEB.API.Common

Common class that any WEB API inherits. Contains methods required for API building:
  • prepare_params - preparing of WEB API method calling parameters
  • extract - extraction of data arrays from response

WEB.API.Auth

Use this class to add to your WEB API OAuth 1.0 (see flickr_example) and OAuth 2.0 (see vk_example) support.

WEB.API.Req

Library for HTTP WEB requests. More handy alternative to builtin webread and webwrite.
For example see req_example
Main functions:
  • seturl - set request base URL
  • addurl - add method to request URL
  • getfullurl - get full URL with query parameters
  • addquery - add a query parameter
  • addbody - add body field
  • addheader - add header field
  • setopts - set request options (see weboptions)
  • get, post, put, delete, patch - perform request
All functions:
doc WEB.API.Req
9 |
10 | 46 |
-------------------------------------------------------------------------------- /doc/WebApi.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/WebApi.mlx -------------------------------------------------------------------------------- /doc/WebUtils.html: -------------------------------------------------------------------------------- 1 | 2 | WEB.Utils

WEB.Utils

Additional libraries for WEB APIs

WEB.Utils.Plotter

Tools for visualization and plotting:

WEB.Utils.Storage

Work with data storage. Data is stored in data variable in *.mat-file.

WEB.Utils.Tables

Additional functions for more convinient work with tables.
7 |
8 | 25 |
-------------------------------------------------------------------------------- /doc/WebUtils.mlx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ETMC-Exponenta/MATLAB-WEB-API/d57a1f6ac0ff60fd65de9254a60f08b5e851aef8/doc/WebUtils.mlx -------------------------------------------------------------------------------- /doc/helptoc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | MATLAB WEB API 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | Reference 23 | WEB.API 24 | WEB.Utils 25 | 26 | Examples 28 | 29 | 30 | 32 | MATLAB WEB API on File Exchange 33 | 34 | 36 | MATLAB WEB API on GitHub 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/bingmaps_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | api_key = '...'; % place here your API Key 3 | %% Create service object 4 | bm = WEB.BingMaps(api_key); 5 | %% Set data path 6 | bm.set_data_path('../data/'); 7 | %% Get location geocode 8 | [res, gcode] = bm.location_findByQuery('moscow', 'maxResults', 1, 'useStorage', 1) 9 | %% Get location geocodes and plot result 10 | [res, gcodes] = bm.location_findByQuery('moscow', 'maxResults', 5, 'plot', 1) 11 | %% Get location address by geocode 12 | res = bm.location_findByPoint([55.7570, 37.6150], 'useStorage', 1) 13 | %% Get geocode by address 14 | [res, gcode] = bm.location_findByAddress('locality', 'RU', 'postalCode', 115088, 'addressLine', 'Moscow, 2-nd Yuzhnoportovy Proyezd, 31', 'maxResults', 1, 'plot', 1) 15 | %% Get local business entities near that location 16 | res = bm.location_recognition([42.2994411, -71.3513379]); 17 | res.businessesAtLocation{1}(1).businessInfo 18 | %% Get Moscow image and save to 'data' folder 19 | bm.imagery_staticMap('aerialwithlabels', 'moscow, city', 'show', 1, 'mapSize', [1000,1000], 'dpi', 'large', 'save', 1, 'name', 'Moscow'); 20 | %% Get horse photo 21 | bm.imagery_staticMap('aerial', [51.1011 1.1395], 'zoomLevel', 19, 'show', 1, 'mapSize', [600, 600]); -------------------------------------------------------------------------------- /examples/datagov_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | api_key = '...'; % place here your API Key 3 | %% Create service object 4 | dg = WEB.DataGov(api_key); 5 | %% Get main page 6 | res = dg.main() 7 | %% Get dataset 8 | res = dg.datasets() 9 | %% Get museum statistics 10 | res = dg.datasets('topic', 'Culture', 'organization', '7705851331') 11 | %% Get dataset info and versions 12 | [res, vers] = dg.dataset('7705851331-stat_museum') 13 | %% Get version data, structure and content 14 | [res, str, cont] = dg.version('7705851331-stat_museum', vers{end}) 15 | %% Get organizations 16 | orgs = dg.organizations() 17 | %% Get organization title 18 | [res, data] = dg.organization('5752056337') 19 | %% Get topics 20 | topics = dg.topics() 21 | %% Get topic datasets 22 | [res, data] = dg.topic('Weather') -------------------------------------------------------------------------------- /examples/flickr_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | app_key = '...'; % place here your App Key 3 | app_secret = '...'; % place here your App Secret 4 | %% Create service object 5 | fl = WEB.Flickr(app_key, app_secret); 6 | %% Set data path 7 | fl.set_data_path('../data/'); 8 | %% Get Token 9 | res = fl.login() 10 | %% Test Login 11 | res = fl.test_login() 12 | %% Test Echo 13 | res = fl.test_echo() 14 | %% Get Photos 15 | photos = fl.groups_getPhotos('967057@N23') 16 | %% Get Available Sizes 17 | sizes = fl.photos_getSizes(photos.photo.id{1}) 18 | %% Get Photo 19 | photo = fl.get_photo(photos.photo.id{1}, 'size', 'medium', 'show', 1); 20 | %% Save Photo 21 | photo = fl.get_photo(photos.photo.id{1}, 'size', 'original', 'show', 1, 'save', 1, 'name', 'pic.jpg'); 22 | %% Search Photos 23 | photos = fl.photos_search('text', 'cat', 'tags', 'cat') 24 | %% Log Out 25 | fl.logout(); -------------------------------------------------------------------------------- /examples/headhunter_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | access_token = '...'; % place here your Access Token 3 | %% Create service object 4 | hh = WEB.HeadHunter(access_token); 5 | %% Create service object without authorization 6 | hh = WEB.HeadHunter(); 7 | %% Get info about me 8 | [res, err] = hh.me() 9 | %% Get dictionaries 10 | dicts = hh.dictionaries() 11 | %% Get industries 12 | inds = hh.industries() 13 | %% Get specializations 14 | specs = hh.specializations() 15 | %% Get areas 16 | areas = hh.areas() 17 | %% Get metro 18 | metro = hh.metro() 19 | %% Search vacancies 20 | res = hh.vacancies('text', 'MATLAB', 'date_from', '2018-07-31', 'per_page', 100) -------------------------------------------------------------------------------- /examples/ip_example.m: -------------------------------------------------------------------------------- 1 | %% Create service object 2 | ip = WEB.IP(); 3 | %% Get my IP info 4 | [info, addr] = ip.get() 5 | %% Get Google info 6 | info = ip.get('8.8.8.8') 7 | %% Get ETMC Exponenta info 8 | [info, addr] = ip.get('exponenta.ru', 'plot') -------------------------------------------------------------------------------- /examples/mps_example.m: -------------------------------------------------------------------------------- 1 | %% Create service object 2 | addr = 'http://
:'; % enter your MATLAB Production Server address 3 | app = '...'; % enter your deployed application name 4 | mps = WEB.MPS(addr, app); 5 | %% Get server health 6 | mps.health() 7 | %% Services discovery 8 | [res, err] = mps.discovery() 9 | %% Execute deployed function 10 | mps.setOutputFormat('mode', 'large'); % set output format (optional) 11 | fcnname = '...'; % deployed function name 12 | argsin = {'...'}; % input arguments 13 | nargsout = 1; % number of output arguments 14 | [res, err] = mps.exec(fcnname, argsin, nargsout) 15 | %% Asynchronous execution 16 | fcnname = '...'; % deployed function name 17 | argsin = {'...'}; % input arguments 18 | nargsout = 1; % number of output arguments 19 | [res, err] = mps.async(fcnname, argsin, nargsout) % create async request 20 | res = mps.information() % get response information and state 21 | res = mps.result() % get response result 22 | mps.delete() % delete request from MPS -------------------------------------------------------------------------------- /examples/netsuite_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | account = '...'; 3 | consumer_key = '...'; 4 | consumer_secret = '...'; 5 | token_id = '...'; 6 | token_secret = '...'; 7 | %% Create service object 8 | ns = WEB.NetSuite(account, consumer_key, consumer_secret, token_id, token_secret) 9 | %% Call API 10 | [res, err] = ns.getEmployee('roslovets@exponenta.ru') -------------------------------------------------------------------------------- /examples/openweathermap_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | api_key = '...'; % place here your API Key 3 | %% Create service object 4 | owm = WEB.OpenWeatherMap(api_key); 5 | %% Get current weather by query 6 | res = owm.current('q', 'moscow,ru', 'units', 'metric') 7 | %% Get current weather by coordinates (Vostok station, Antarctica) 8 | res = owm.current('lat', -78.464167, 'lon', 106.837222, 'units', 'metric', 'show', true) 9 | %% Get weather forecast (The MathWorks, Natick) 10 | [res, data] = owm.forecast('Zip', '01760,us', 'units', 'metric', 'show', true) -------------------------------------------------------------------------------- /examples/req_example.m: -------------------------------------------------------------------------------- 1 | % Req - library for WEB Requests. It's core component for all WEB APIs. 2 | % Req can be usefull as standalone library. 3 | %% Create Request 4 | req = WEB.API.Req('http://uinames.com/api/'); 5 | %% Add query options 6 | req.addquery('region', 'germany'); 7 | req.addquery('gender', 'female'); 8 | %% Set options 9 | req.setopts('ContentType', 'json'); 10 | req.setopts('Timeout', 10); 11 | %% Perform GET request 12 | res = get(req) 13 | %% Add more options 14 | req.addquery('minlen', 20); 15 | req.addquery('amount', 5); 16 | req.addquery('gender', 'male'); 17 | res = get(req); 18 | res = struct2table(res) 19 | %% Explore req object 20 | req 21 | req.getquery() 22 | req.getfullurl() 23 | req.getopts() 24 | %% Req documentation 25 | doc(req) -------------------------------------------------------------------------------- /examples/restcountries_example.m: -------------------------------------------------------------------------------- 1 | %% Create service object 2 | rc = WEB.RESTCountries(); 3 | %% Get all info 4 | res = rc.all() 5 | res = rc.all('fields', 'name;capital;region;population;area') 6 | %% Get by country name 7 | res = rc.byName('Russia') 8 | res = rc.byName('Russian Federation', 'fullText', true) 9 | %% Get by code 10 | res = rc.byCode('RU') 11 | res = rc.byCode({'US','GB'}) 12 | %% Get by currency code 13 | res = rc.byCurrency('USD') 14 | %% Get by region 15 | res = rc.byRegion('Africa') 16 | res = rc.byRegion('Asia', 'fields', 'name;capital') -------------------------------------------------------------------------------- /examples/uinames_example.m: -------------------------------------------------------------------------------- 1 | %% Create service object 2 | uin = WEB.Uinames(); 3 | %% Get random name 4 | res = uin.get() 5 | %% Get 5 names 6 | res = uin.get('amount', 5) 7 | %% Get 5 russian names 8 | res = uin.get('amount', 5, 'region', 'russia') 9 | %% Get long name 10 | res = uin.get('minlen', 55) 11 | %% Get short name 12 | res = uin.get('maxlen', 6) -------------------------------------------------------------------------------- /examples/vk_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | client_id = '...'; % place here your Client ID 3 | client_secret = '...'; % place here your Client Secret 4 | scope = {'friends', 'pages', 'groups', 'wall'}; % selective scope 5 | % scope = 'all'; 6 | % scope = 140492255 % all 7 | %% Create service object 8 | vk = WEB.VK(client_id, scope, client_secret); 9 | %% Set data path 10 | vk.set_data_path('../data/'); 11 | %% Login to VK.com 12 | vk.login() 13 | %% Get friends 14 | [res, count] = vk.friends_get('852372', 'fields', 'nickname') 15 | %% Groups search 16 | [res, count] = vk.groups_search('MATLAB', 'type', 'page', 'sort', 1) 17 | %% Get all group members 18 | [res, count] = vk.groups_getMembers('41030489', 'fields', 'city,sex', 'getAll', 1) 19 | %% Get user followers 20 | [res, count] = vk.users_getFollowers('1', 'fields', 'city,sex', 'count', 1000) 21 | %% Get user subscriptions 22 | res = vk.users_getSubscriptions('10050301', 'extended', true, 'extract', true) 23 | %% Log out 24 | vk.logout(); -------------------------------------------------------------------------------- /examples/youtube_example.m: -------------------------------------------------------------------------------- 1 | %% Set auth data 2 | key = '...'; % place here your YouTube API v3 Key 3 | %% Create service object 4 | yt = WEB.YouTube(key); 5 | %% Search videos 6 | v = yt.search('q', 'matlab getting started') 7 | %% Full list of search options 8 | [v, res, err] = yt.search('q', 'matlab', 'channelId', 'UCgdHSFcXvkN6O3NXvif0-pA', 'maxResults', 50, 'order', 'date') -------------------------------------------------------------------------------- /info.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2018a 4 | MATLAB WEB API 5 | toolbox 6 | 7 | doc 8 | 9 | -------------------------------------------------------------------------------- /install.m: -------------------------------------------------------------------------------- 1 | function install 2 | % Generated with Toolbox Extender https://github.com/ETMC-Exponenta/ToolboxExtender 3 | dev = MATLABWEBAPIDev; 4 | dev.test('', false); 5 | % Post-install commands 6 | cd('..'); 7 | ext = MATLABWEBAPIExtender; 8 | ext.doc; 9 | % Add your post-install commands below -------------------------------------------------------------------------------- /installweb.m: -------------------------------------------------------------------------------- 1 | % Generated with Toolbox Extender https://github.com/ETMC-Exponenta/ToolboxExtender 2 | instURL = 'https://api.github.com/repos/ETMC-Exponenta/MATLAB-WEB-API/releases/latest'; 3 | [~, instName] = fileparts(fileparts(fileparts(instURL))); 4 | instRes = webread(instURL); 5 | fprintf('Downloading %s %s\n', instName, instRes.name); 6 | websave(instRes.assets.name, instRes.assets.browser_download_url); 7 | disp('Installing...') 8 | matlab.addons.install(instRes.assets.name); 9 | clear instURL instRes instName 10 | disp('Installation complete!') 11 | % Post-install commands 12 | ext = MATLABWEBAPIExtender; 13 | ext.doc; 14 | clear ext 15 | % Add your post-install commands below --------------------------------------------------------------------------------