├── .gitignore
├── Pipfile
├── Pipfile.lock
├── Procfile
├── README.md
├── app.json
├── app.py
├── runtime.txt
├── templates
└── index.html
├── utils.py
└── variables.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | dist
3 | build
4 | *.pyc
5 | *.pyo
6 | *.swp
7 | *.swa
8 | *.egg-info
9 | .env
10 | tconnectsync-check-output.log
11 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [dev-packages]
7 |
8 | [packages]
9 | flask = "*"
10 | tconnectsync = ">=2.1.3"
11 | flask-apscheduler = {git = "https://github.com/viniciuschiele/flask-apscheduler"}
12 | gunicorn = "*"
13 |
14 | [requires]
15 | python_version = "3.9"
16 |
17 | [scripts]
18 | web = "gunicorn app:app"
19 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "72781934f0fe0fe99080977e39d9066c8ef8758d3808786e3c057d029103a7ce"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.9"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "apscheduler": {
20 | "hashes": [
21 | "sha256:e6df071b27d9be898e486bc7940a7be50b4af2e9da7c08f0744a96d4bd4cef4a",
22 | "sha256:fb91e8a768632a4756a585f79ec834e0e27aad5860bac7eaa523d9ccefd87661"
23 | ],
24 | "markers": "python_version >= '3.6'",
25 | "version": "==3.10.4"
26 | },
27 | "arrow": {
28 | "hashes": [
29 | "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80",
30 | "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"
31 | ],
32 | "markers": "python_version >= '3.8'",
33 | "version": "==1.3.0"
34 | },
35 | "beautifulsoup4": {
36 | "hashes": [
37 | "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051",
38 | "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"
39 | ],
40 | "markers": "python_full_version >= '3.6.0'",
41 | "version": "==4.12.3"
42 | },
43 | "blinker": {
44 | "hashes": [
45 | "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01",
46 | "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"
47 | ],
48 | "markers": "python_version >= '3.8'",
49 | "version": "==1.8.2"
50 | },
51 | "bs4": {
52 | "hashes": [
53 | "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925",
54 | "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"
55 | ],
56 | "version": "==0.0.2"
57 | },
58 | "certifi": {
59 | "hashes": [
60 | "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8",
61 | "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"
62 | ],
63 | "markers": "python_version >= '3.6'",
64 | "version": "==2024.8.30"
65 | },
66 | "cffi": {
67 | "hashes": [
68 | "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8",
69 | "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2",
70 | "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1",
71 | "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15",
72 | "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36",
73 | "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824",
74 | "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8",
75 | "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36",
76 | "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17",
77 | "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf",
78 | "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc",
79 | "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3",
80 | "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed",
81 | "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702",
82 | "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1",
83 | "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8",
84 | "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903",
85 | "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6",
86 | "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d",
87 | "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b",
88 | "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e",
89 | "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be",
90 | "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c",
91 | "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683",
92 | "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9",
93 | "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c",
94 | "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8",
95 | "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1",
96 | "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4",
97 | "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655",
98 | "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67",
99 | "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595",
100 | "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0",
101 | "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65",
102 | "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41",
103 | "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6",
104 | "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401",
105 | "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6",
106 | "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3",
107 | "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16",
108 | "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93",
109 | "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e",
110 | "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4",
111 | "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964",
112 | "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c",
113 | "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576",
114 | "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0",
115 | "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3",
116 | "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662",
117 | "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3",
118 | "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff",
119 | "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5",
120 | "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd",
121 | "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f",
122 | "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5",
123 | "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14",
124 | "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d",
125 | "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9",
126 | "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7",
127 | "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382",
128 | "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a",
129 | "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e",
130 | "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a",
131 | "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4",
132 | "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99",
133 | "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87",
134 | "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"
135 | ],
136 | "markers": "python_version >= '3.8'",
137 | "version": "==1.17.1"
138 | },
139 | "charset-normalizer": {
140 | "hashes": [
141 | "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027",
142 | "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087",
143 | "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786",
144 | "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8",
145 | "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09",
146 | "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185",
147 | "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574",
148 | "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e",
149 | "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519",
150 | "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898",
151 | "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269",
152 | "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3",
153 | "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f",
154 | "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6",
155 | "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8",
156 | "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a",
157 | "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73",
158 | "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc",
159 | "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714",
160 | "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2",
161 | "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc",
162 | "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce",
163 | "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d",
164 | "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e",
165 | "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6",
166 | "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269",
167 | "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96",
168 | "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d",
169 | "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a",
170 | "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4",
171 | "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77",
172 | "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d",
173 | "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0",
174 | "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed",
175 | "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068",
176 | "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac",
177 | "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25",
178 | "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8",
179 | "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab",
180 | "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26",
181 | "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2",
182 | "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db",
183 | "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f",
184 | "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5",
185 | "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99",
186 | "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c",
187 | "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d",
188 | "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811",
189 | "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa",
190 | "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a",
191 | "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03",
192 | "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b",
193 | "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04",
194 | "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c",
195 | "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001",
196 | "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458",
197 | "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389",
198 | "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99",
199 | "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985",
200 | "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537",
201 | "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238",
202 | "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f",
203 | "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d",
204 | "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796",
205 | "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a",
206 | "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143",
207 | "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8",
208 | "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c",
209 | "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5",
210 | "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5",
211 | "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711",
212 | "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4",
213 | "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6",
214 | "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c",
215 | "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7",
216 | "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4",
217 | "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b",
218 | "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae",
219 | "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12",
220 | "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c",
221 | "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae",
222 | "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8",
223 | "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887",
224 | "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b",
225 | "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4",
226 | "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f",
227 | "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5",
228 | "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33",
229 | "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519",
230 | "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"
231 | ],
232 | "markers": "python_full_version >= '3.7.0'",
233 | "version": "==3.3.2"
234 | },
235 | "click": {
236 | "hashes": [
237 | "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
238 | "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"
239 | ],
240 | "markers": "python_version >= '3.7'",
241 | "version": "==8.1.7"
242 | },
243 | "cryptography": {
244 | "hashes": [
245 | "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494",
246 | "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806",
247 | "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d",
248 | "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062",
249 | "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2",
250 | "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4",
251 | "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1",
252 | "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85",
253 | "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84",
254 | "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042",
255 | "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d",
256 | "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962",
257 | "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2",
258 | "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa",
259 | "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d",
260 | "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365",
261 | "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96",
262 | "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47",
263 | "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d",
264 | "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d",
265 | "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c",
266 | "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb",
267 | "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277",
268 | "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172",
269 | "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034",
270 | "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a",
271 | "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"
272 | ],
273 | "markers": "python_version >= '3.7'",
274 | "version": "==43.0.1"
275 | },
276 | "dataclasses-json": {
277 | "hashes": [
278 | "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a",
279 | "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"
280 | ],
281 | "markers": "python_version >= '3.7' and python_version < '4.0'",
282 | "version": "==0.6.7"
283 | },
284 | "flask": {
285 | "hashes": [
286 | "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3",
287 | "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"
288 | ],
289 | "index": "pypi",
290 | "markers": "python_version >= '3.8'",
291 | "version": "==3.0.3"
292 | },
293 | "flask-apscheduler": {
294 | "git": "https://github.com/viniciuschiele/flask-apscheduler",
295 | "markers": "python_version >= '3.8'",
296 | "ref": "d5b6cdd10415a413b2773b7750d56b7dfed68018"
297 | },
298 | "gunicorn": {
299 | "hashes": [
300 | "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d",
301 | "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"
302 | ],
303 | "index": "pypi",
304 | "markers": "python_version >= '3.7'",
305 | "version": "==23.0.0"
306 | },
307 | "idna": {
308 | "hashes": [
309 | "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9",
310 | "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"
311 | ],
312 | "markers": "python_version >= '3.6'",
313 | "version": "==3.10"
314 | },
315 | "importlib-metadata": {
316 | "hashes": [
317 | "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b",
318 | "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"
319 | ],
320 | "markers": "python_version < '3.10'",
321 | "version": "==8.5.0"
322 | },
323 | "itsdangerous": {
324 | "hashes": [
325 | "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef",
326 | "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"
327 | ],
328 | "markers": "python_version >= '3.8'",
329 | "version": "==2.2.0"
330 | },
331 | "jinja2": {
332 | "hashes": [
333 | "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369",
334 | "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"
335 | ],
336 | "markers": "python_version >= '3.7'",
337 | "version": "==3.1.4"
338 | },
339 | "lxml": {
340 | "hashes": [
341 | "sha256:01220dca0d066d1349bd6a1726856a78f7929f3878f7e2ee83c296c69495309e",
342 | "sha256:02ced472497b8362c8e902ade23e3300479f4f43e45f4105c85ef43b8db85229",
343 | "sha256:052d99051e77a4f3e8482c65014cf6372e61b0a6f4fe9edb98503bb5364cfee3",
344 | "sha256:07da23d7ee08577760f0a71d67a861019103e4812c87e2fab26b039054594cc5",
345 | "sha256:094cb601ba9f55296774c2d57ad68730daa0b13dc260e1f941b4d13678239e70",
346 | "sha256:0a7056921edbdd7560746f4221dca89bb7a3fe457d3d74267995253f46343f15",
347 | "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002",
348 | "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd",
349 | "sha256:0fdf3a3059611f7585a78ee10399a15566356116a4288380921a4b598d807a22",
350 | "sha256:109fa6fede314cc50eed29e6e56c540075e63d922455346f11e4d7a036d2b8cf",
351 | "sha256:146173654d79eb1fc97498b4280c1d3e1e5d58c398fa530905c9ea50ea849b22",
352 | "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832",
353 | "sha256:1483fd3358963cc5c1c9b122c80606a3a79ee0875bcac0204149fa09d6ff2727",
354 | "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e",
355 | "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30",
356 | "sha256:18feb4b93302091b1541221196a2155aa296c363fd233814fa11e181adebc52f",
357 | "sha256:1afe0a8c353746e610bd9031a630a95bcfb1a720684c3f2b36c4710a0a96528f",
358 | "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51",
359 | "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4",
360 | "sha256:1ffc23010330c2ab67fac02781df60998ca8fe759e8efde6f8b756a20599c5de",
361 | "sha256:20094fc3f21ea0a8669dc4c61ed7fa8263bd37d97d93b90f28fc613371e7a875",
362 | "sha256:213261f168c5e1d9b7535a67e68b1f59f92398dd17a56d934550837143f79c42",
363 | "sha256:218c1b2e17a710e363855594230f44060e2025b05c80d1f0661258142b2add2e",
364 | "sha256:23e0553b8055600b3bf4a00b255ec5c92e1e4aebf8c2c09334f8368e8bd174d6",
365 | "sha256:25f1b69d41656b05885aa185f5fdf822cb01a586d1b32739633679699f220391",
366 | "sha256:2b3778cb38212f52fac9fe913017deea2fdf4eb1a4f8e4cfc6b009a13a6d3fcc",
367 | "sha256:2bc9fd5ca4729af796f9f59cd8ff160fe06a474da40aca03fcc79655ddee1a8b",
368 | "sha256:2c226a06ecb8cdef28845ae976da407917542c5e6e75dcac7cc33eb04aaeb237",
369 | "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4",
370 | "sha256:2c86bf781b12ba417f64f3422cfc302523ac9cd1d8ae8c0f92a1c66e56ef2e86",
371 | "sha256:2d9b8d9177afaef80c53c0a9e30fa252ff3036fb1c6494d427c066a4ce6a282f",
372 | "sha256:2dec2d1130a9cda5b904696cec33b2cfb451304ba9081eeda7f90f724097300a",
373 | "sha256:2dfab5fa6a28a0b60a20638dc48e6343c02ea9933e3279ccb132f555a62323d8",
374 | "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f",
375 | "sha256:315f9542011b2c4e1d280e4a20ddcca1761993dda3afc7a73b01235f8641e903",
376 | "sha256:36aef61a1678cb778097b4a6eeae96a69875d51d1e8f4d4b491ab3cfb54b5a03",
377 | "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e",
378 | "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99",
379 | "sha256:3c174dc350d3ec52deb77f2faf05c439331d6ed5e702fc247ccb4e6b62d884b7",
380 | "sha256:3eb44520c4724c2e1a57c0af33a379eee41792595023f367ba3952a2d96c2aab",
381 | "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d",
382 | "sha256:41ce1f1e2c7755abfc7e759dc34d7d05fd221723ff822947132dc934d122fe22",
383 | "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492",
384 | "sha256:44264ecae91b30e5633013fb66f6ddd05c006d3e0e884f75ce0b4755b3e3847b",
385 | "sha256:482c2f67761868f0108b1743098640fbb2a28a8e15bf3f47ada9fa59d9fe08c3",
386 | "sha256:4b0c7a688944891086ba192e21c5229dea54382f4836a209ff8d0a660fac06be",
387 | "sha256:4c1fefd7e3d00921c44dc9ca80a775af49698bbfd92ea84498e56acffd4c5469",
388 | "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f",
389 | "sha256:501d0d7e26b4d261fca8132854d845e4988097611ba2531408ec91cf3fd9d20a",
390 | "sha256:516f491c834eb320d6c843156440fe7fc0d50b33e44387fcec5b02f0bc118a4c",
391 | "sha256:51806cfe0279e06ed8500ce19479d757db42a30fd509940b1701be9c86a5ff9a",
392 | "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4",
393 | "sha256:56b9861a71575f5795bde89256e7467ece3d339c9b43141dbdd54544566b3b94",
394 | "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442",
395 | "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b",
396 | "sha256:5c54afdcbb0182d06836cc3d1be921e540be3ebdf8b8a51ee3ef987537455f84",
397 | "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c",
398 | "sha256:609251a0ca4770e5a8768ff902aa02bf636339c5a93f9349b48eb1f606f7f3e9",
399 | "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1",
400 | "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be",
401 | "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367",
402 | "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e",
403 | "sha256:68934b242c51eb02907c5b81d138cb977b2129a0a75a8f8b60b01cb8586c7b21",
404 | "sha256:68b87753c784d6acb8a25b05cb526c3406913c9d988d51f80adecc2b0775d6aa",
405 | "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16",
406 | "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d",
407 | "sha256:6b038cc86b285e4f9fea2ba5ee76e89f21ed1ea898e287dc277a25884f3a7dfe",
408 | "sha256:6ba0d3dcac281aad8a0e5b14c7ed6f9fa89c8612b47939fc94f80b16e2e9bc83",
409 | "sha256:6e91cf736959057f7aac7adfc83481e03615a8e8dd5758aa1d95ea69e8931dba",
410 | "sha256:6ee8c39582d2652dcd516d1b879451500f8db3fe3607ce45d7c5957ab2596040",
411 | "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763",
412 | "sha256:71a8dd38fbd2f2319136d4ae855a7078c69c9a38ae06e0c17c73fd70fc6caad8",
413 | "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff",
414 | "sha256:7437237c6a66b7ca341e868cda48be24b8701862757426852c9b3186de1da8a2",
415 | "sha256:747a3d3e98e24597981ca0be0fd922aebd471fa99d0043a3842d00cdcad7ad6a",
416 | "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b",
417 | "sha256:78d9b952e07aed35fe2e1a7ad26e929595412db48535921c5013edc8aa4a35ce",
418 | "sha256:7b1cd427cb0d5f7393c31b7496419da594fe600e6fdc4b105a54f82405e6626c",
419 | "sha256:7d3d1ca42870cdb6d0d29939630dbe48fa511c203724820fc0fd507b2fb46577",
420 | "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8",
421 | "sha256:7f41026c1d64043a36fda21d64c5026762d53a77043e73e94b71f0521939cc71",
422 | "sha256:81b4e48da4c69313192d8c8d4311e5d818b8be1afe68ee20f6385d0e96fc9512",
423 | "sha256:86a6b24b19eaebc448dc56b87c4865527855145d851f9fc3891673ff97950540",
424 | "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f",
425 | "sha256:89e043f1d9d341c52bf2af6d02e6adde62e0a46e6755d5eb60dc6e4f0b8aeca2",
426 | "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a",
427 | "sha256:8dc2c0395bea8254d8daebc76dcf8eb3a95ec2a46fa6fae5eaccee366bfe02ce",
428 | "sha256:8f0de2d390af441fe8b2c12626d103540b5d850d585b18fcada58d972b74a74e",
429 | "sha256:92e67a0be1639c251d21e35fe74df6bcc40cba445c2cda7c4a967656733249e2",
430 | "sha256:94d6c3782907b5e40e21cadf94b13b0842ac421192f26b84c45f13f3c9d5dc27",
431 | "sha256:97acf1e1fd66ab53dacd2c35b319d7e548380c2e9e8c54525c6e76d21b1ae3b1",
432 | "sha256:9ada35dd21dc6c039259596b358caab6b13f4db4d4a7f8665764d616daf9cc1d",
433 | "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1",
434 | "sha256:9e41506fec7a7f9405b14aa2d5c8abbb4dbbd09d88f9496958b6d00cb4d45330",
435 | "sha256:9e4b47ac0f5e749cfc618efdf4726269441014ae1d5583e047b452a32e221920",
436 | "sha256:9fb81d2824dff4f2e297a276297e9031f46d2682cafc484f49de182aa5e5df99",
437 | "sha256:a0eabd0a81625049c5df745209dc7fcef6e2aea7793e5f003ba363610aa0a3ff",
438 | "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18",
439 | "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff",
440 | "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c",
441 | "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179",
442 | "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080",
443 | "sha256:ace2c2326a319a0bb8a8b0e5b570c764962e95818de9f259ce814ee666603f19",
444 | "sha256:ae5fe5c4b525aa82b8076c1a59d642c17b6e8739ecf852522c6321852178119d",
445 | "sha256:b11a5d918a6216e521c715b02749240fb07ae5a1fefd4b7bf12f833bc8b4fe70",
446 | "sha256:b1c8c20847b9f34e98080da785bb2336ea982e7f913eed5809e5a3c872900f32",
447 | "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a",
448 | "sha256:b710bc2b8292966b23a6a0121f7a6c51d45d2347edcc75f016ac123b8054d3f2",
449 | "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79",
450 | "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3",
451 | "sha256:c162b216070f280fa7da844531169be0baf9ccb17263cf5a8bf876fcd3117fa5",
452 | "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f",
453 | "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d",
454 | "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3",
455 | "sha256:c300306673aa0f3ed5ed9372b21867690a17dba38c68c44b287437c362ce486b",
456 | "sha256:c56a1d43b2f9ee4786e4658c7903f05da35b923fb53c11025712562d5cc02753",
457 | "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9",
458 | "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957",
459 | "sha256:cb83f8a875b3d9b458cada4f880fa498646874ba4011dc974e071a0a84a1b033",
460 | "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb",
461 | "sha256:dd36439be765e2dde7660212b5275641edbc813e7b24668831a5c8ac91180656",
462 | "sha256:dd5350b55f9fecddc51385463a4f67a5da829bc741e38cf689f38ec9023f54ab",
463 | "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b",
464 | "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d",
465 | "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd",
466 | "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859",
467 | "sha256:ea2e2f6f801696ad7de8aec061044d6c8c0dd4037608c7cab38a9a4d316bfb11",
468 | "sha256:eafa2c8658f4e560b098fe9fc54539f86528651f61849b22111a9b107d18910c",
469 | "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a",
470 | "sha256:ee70d08fd60c9565ba8190f41a46a54096afa0eeb8f76bd66f2c25d3b1b83005",
471 | "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654",
472 | "sha256:ef0c1fe22171dd7c7c27147f2e9c3e86f8bdf473fed75f16b0c2e84a5030ce80",
473 | "sha256:f2901429da1e645ce548bf9171784c0f74f0718c3f6150ce166be39e4dd66c3e",
474 | "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec",
475 | "sha256:f65e5120863c2b266dbcc927b306c5b78e502c71edf3295dfcb9501ec96e5fc7",
476 | "sha256:f7d4a670107d75dfe5ad080bed6c341d18c4442f9378c9f58e5851e86eb79965",
477 | "sha256:f914c03e6a31deb632e2daa881fe198461f4d06e57ac3d0e05bbcab8eae01945",
478 | "sha256:fb66442c2546446944437df74379e9cf9e9db353e61301d1a0e26482f43f0dd8"
479 | ],
480 | "markers": "python_version >= '3.6'",
481 | "version": "==5.3.0"
482 | },
483 | "markupsafe": {
484 | "hashes": [
485 | "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf",
486 | "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff",
487 | "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f",
488 | "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3",
489 | "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532",
490 | "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f",
491 | "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617",
492 | "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df",
493 | "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4",
494 | "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906",
495 | "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f",
496 | "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4",
497 | "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8",
498 | "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371",
499 | "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2",
500 | "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465",
501 | "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52",
502 | "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6",
503 | "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169",
504 | "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad",
505 | "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2",
506 | "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0",
507 | "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029",
508 | "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f",
509 | "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a",
510 | "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced",
511 | "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5",
512 | "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c",
513 | "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf",
514 | "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9",
515 | "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb",
516 | "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad",
517 | "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3",
518 | "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1",
519 | "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46",
520 | "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc",
521 | "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a",
522 | "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee",
523 | "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900",
524 | "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5",
525 | "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea",
526 | "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f",
527 | "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5",
528 | "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e",
529 | "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a",
530 | "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f",
531 | "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50",
532 | "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a",
533 | "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b",
534 | "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4",
535 | "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff",
536 | "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2",
537 | "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46",
538 | "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b",
539 | "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf",
540 | "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5",
541 | "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5",
542 | "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab",
543 | "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd",
544 | "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"
545 | ],
546 | "markers": "python_version >= '3.7'",
547 | "version": "==2.1.5"
548 | },
549 | "marshmallow": {
550 | "hashes": [
551 | "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e",
552 | "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"
553 | ],
554 | "markers": "python_version >= '3.8'",
555 | "version": "==3.22.0"
556 | },
557 | "mypy-extensions": {
558 | "hashes": [
559 | "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d",
560 | "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"
561 | ],
562 | "markers": "python_version >= '3.5'",
563 | "version": "==1.0.0"
564 | },
565 | "oauthlib": {
566 | "hashes": [
567 | "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca",
568 | "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"
569 | ],
570 | "markers": "python_version >= '3.6'",
571 | "version": "==3.2.2"
572 | },
573 | "packaging": {
574 | "hashes": [
575 | "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002",
576 | "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"
577 | ],
578 | "markers": "python_version >= '3.8'",
579 | "version": "==24.1"
580 | },
581 | "platformdirs": {
582 | "hashes": [
583 | "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3",
584 | "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"
585 | ],
586 | "markers": "python_version >= '3.7'",
587 | "version": "==3.11.0"
588 | },
589 | "pycparser": {
590 | "hashes": [
591 | "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6",
592 | "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"
593 | ],
594 | "markers": "python_version >= '3.8'",
595 | "version": "==2.22"
596 | },
597 | "pyjwt": {
598 | "hashes": [
599 | "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850",
600 | "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"
601 | ],
602 | "markers": "python_version >= '3.8'",
603 | "version": "==2.9.0"
604 | },
605 | "pypng": {
606 | "hashes": [
607 | "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c",
608 | "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"
609 | ],
610 | "version": "==0.20220715.0"
611 | },
612 | "pysocks": {
613 | "hashes": [
614 | "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299",
615 | "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5",
616 | "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"
617 | ],
618 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
619 | "version": "==1.7.1"
620 | },
621 | "python-dateutil": {
622 | "hashes": [
623 | "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3",
624 | "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"
625 | ],
626 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
627 | "version": "==2.9.0.post0"
628 | },
629 | "python-dotenv": {
630 | "hashes": [
631 | "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca",
632 | "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
633 | ],
634 | "markers": "python_version >= '3.8'",
635 | "version": "==1.0.1"
636 | },
637 | "pytz": {
638 | "hashes": [
639 | "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a",
640 | "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"
641 | ],
642 | "version": "==2024.2"
643 | },
644 | "qrcode": {
645 | "hashes": [
646 | "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a",
647 | "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"
648 | ],
649 | "markers": "python_version >= '3.7'",
650 | "version": "==7.4.2"
651 | },
652 | "requests": {
653 | "hashes": [
654 | "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760",
655 | "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"
656 | ],
657 | "markers": "python_version >= '3.8'",
658 | "version": "==2.32.3"
659 | },
660 | "requests-mock": {
661 | "hashes": [
662 | "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563",
663 | "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401"
664 | ],
665 | "markers": "python_version >= '3.5'",
666 | "version": "==1.12.1"
667 | },
668 | "requests-oauthlib": {
669 | "hashes": [
670 | "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5",
671 | "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"
672 | ],
673 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
674 | "version": "==1.3.1"
675 | },
676 | "requests-oidc": {
677 | "hashes": [
678 | "sha256:227fbcc561d0acb54469d62a7d4bde055a8eeee7968cdb98fa732e0d2313d814",
679 | "sha256:84ea777457067f8f67fe25534b9c153f3f9a45336c83e073a93ce735a86406ca"
680 | ],
681 | "markers": "python_version >= '3.7' and python_version < '4'",
682 | "version": "==0.6.2"
683 | },
684 | "six": {
685 | "hashes": [
686 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
687 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
688 | ],
689 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
690 | "version": "==1.16.0"
691 | },
692 | "soupsieve": {
693 | "hashes": [
694 | "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb",
695 | "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"
696 | ],
697 | "markers": "python_version >= '3.8'",
698 | "version": "==2.6"
699 | },
700 | "tconnectsync": {
701 | "hashes": [
702 | "sha256:1bdc955e95219b85a191029eabd460f522c7d672f21bea3d0c9b6cb02dc11e1f",
703 | "sha256:4b28ff1449cf8b6db70be01ad4e7d80ec4cc028a37fa73601e1a0871f81b336e"
704 | ],
705 | "index": "pypi",
706 | "markers": "python_version >= '3.7'",
707 | "version": "==2.1.3"
708 | },
709 | "typer": {
710 | "hashes": [
711 | "sha256:aa6c4a4e2329d868b80ecbaf16f807f2b54e192209d7ac9dd42691d63f7a54eb",
712 | "sha256:f714c2d90afae3a7929fcd72a3abb08df305e1ff61719381384211c4070af57f"
713 | ],
714 | "markers": "python_version >= '3.6'",
715 | "version": "==0.9.4"
716 | },
717 | "types-python-dateutil": {
718 | "hashes": [
719 | "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d",
720 | "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"
721 | ],
722 | "markers": "python_version >= '3.8'",
723 | "version": "==2.9.0.20241003"
724 | },
725 | "typing-extensions": {
726 | "hashes": [
727 | "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
728 | "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
729 | ],
730 | "markers": "python_version >= '3.8'",
731 | "version": "==4.12.2"
732 | },
733 | "typing-inspect": {
734 | "hashes": [
735 | "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f",
736 | "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"
737 | ],
738 | "version": "==0.9.0"
739 | },
740 | "tzlocal": {
741 | "hashes": [
742 | "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8",
743 | "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"
744 | ],
745 | "markers": "python_version >= '3.8'",
746 | "version": "==5.2"
747 | },
748 | "urllib3": {
749 | "hashes": [
750 | "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac",
751 | "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"
752 | ],
753 | "markers": "python_version >= '3.8'",
754 | "version": "==2.2.3"
755 | },
756 | "werkzeug": {
757 | "hashes": [
758 | "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c",
759 | "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"
760 | ],
761 | "markers": "python_version >= '3.8'",
762 | "version": "==3.0.4"
763 | },
764 | "zipp": {
765 | "hashes": [
766 | "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350",
767 | "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"
768 | ],
769 | "markers": "python_version >= '3.8'",
770 | "version": "==3.20.2"
771 | }
772 | },
773 | "develop": {}
774 | }
775 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn app:app
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TConnectSync for Heroku
2 |
3 | > **Warning**
4 | > **Heroku discontinued their Free Tier effective November 28, 2022.**
5 | >
6 | > The instructions below will still work on paid Heroku plans.
7 | > For a free alternative, [you can migrate to fly.io](https://fly.io/launch/heroku).
8 | >
9 | > More detailed instructions for using Fly and other Heroku alternatives are not currently available but may be added at a later date.
10 | >
11 | > If you need support, [open an issue on tconnectsync with the `heroku` tag.](https://github.com/jwoglom/tconnectsync/issues/new/choose)
12 |
13 | [](https://heroku.com/deploy?template=https://github.com/jwoglom/tconnectsync-heroku)
14 |
15 | This project allows [tconnectsync][tconnectsync] to run in Heroku.
16 | Unfortunately, the Heroku Free plan is no longer available, so you will need to configure billing in Heroku and pay $5/month ([see Heroku pricing details](https://www.heroku.com/pricing)).
17 |
18 | **Note:** GitHub Issues have been disabled in this repository. To report or investigate issues, [use the GitHub Issues page for tconnectsync](https://github.com/jwoglom/tconnectsync/issues).
19 |
20 | ## Setup
21 |
22 | Before beginning, make sure you have set up the t:connect
23 | [Android](https://play.google.com/store/apps/details?id=com.tandemdiabetes.tconnect&hl=en_US&gl=US),
24 | or [iPhone/iOS](https://apps.apple.com/us/app/t-connect-mobile/id1455916023) application.
25 | If you have not used either, you can find more information or create an account [at the t:connect website]([t:connect web](https://tconnect.tandemdiabetes.com/).
26 |
27 | You'll need the following information:
28 |
29 | * Your t:connect username and password
30 | * Your t:slim X2 pump serial number (visible in Settings > My Pump > Pump Info)
31 | * Your Nightscout site URL
32 | * Your Nightscout site secret
33 |
34 | If you have not yet set up a Nightscout site, [view the Nightscout documentation and set that up first](https://nightscout.github.io/nightscout/new_user/), then come back to these instructions.
35 | Either log in to your existing Heroku account or create a new one.
36 |
37 | Next, you'll be creating a new application inside your Heroku account.
38 | This will not be replacing your existing Heroku Nightscout app, if you have one; you will
39 | be creating a second app with a new name.
40 | **Start by clicking the Deploy button below:**
41 |
42 | [](https://heroku.com/deploy?template=https://github.com/jwoglom/tconnectsync-heroku)
43 |
44 | For *App name*, you can enter any value.
45 | We recommend **`MyNightscoutSite-tconnect`**.
46 |
47 | The app name determines the URL to access your tconnectsync-heroku website, e.g. if you enter the app name `MyNightscoutSite-tconnect`, then the URL for tconnectsync-heroku will be `https://MyNightscoutSite-tconnect.herokuapp.com`.
48 |
49 | The remainder of the options are [tconnectsync environment variables][tconnect-installation]:
50 |
51 | * `TCONNECT_EMAIL` - Your t:connect email
52 | * `TCONNECT_PASSWORD` - Your t:connect password
53 | * `NS_URL` - Your Nightscout site URL (e.g. https://yournightscoutsite.herokuapp.com)
54 | * `NS_SECRET` - Your Nightscout `API_SECRET` value
55 | * `PUMP_SERIAL_NUMBER` - The numeric serial number of your pump. Enter only the number, do not include '#'
56 | * `TIMEZONE_NAME` - The timezone code in which your phone and pump's time is set. ([View a full list of valid values](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones))
57 |
58 | A new variable called `TCONNECTSYNC_HEROKU_SECRET` will be automatically generated for you.
59 | This secret password will be used to access API endpoints which trigger your pump data from tconnect
60 | to be synchronized to Nightscout, as well as view the current status of synchronization.
61 | (You can think of it like the tconnectsync equivalent to Nightscout's `API_SECRET`.)
62 | You can find its automatically generated value by going to Settings > Reveal Config Vars in your Heroku dashboard after setup.
63 |
64 | Once you've filled out the Create New App form and pressed *Deploy App*, you'll be taken to the Heroku dashboard.
65 | From here, you can click on **Open App** to see whether the tconnectsync-heroku server is running.
66 |
67 |
68 |
69 | If you see a page like the following, then you're ready to move on!
70 |
71 |
72 |
73 | If you get an error, then jump to the instructions in the **Testing** section below.
74 |
75 | Otherwise, continue reading below to set up tconnectsync-heroku by creating an account with a separate service, **UptimeRobot**.
76 |
77 | ## UptimeRobot
78 |
79 | Even though your Heroku app is already up and running, we need to set up a service called **UptimeRobot**
80 | which will send a request to your Heroku app to keep it alive so that it can continue to process
81 | your incoming t:connect data in the background.
82 |
83 | ### Creating an Account
84 | Create an account at [UptimeRobot][uptimerobot] by going to https://uptimerobot.com.
85 |
86 |
87 |
88 |
89 |
90 | Once you register, you will be prompted to verify your email address.
91 | Click the link you receive from UptimeRobot in your email.
92 |
93 |
94 |
95 | If you're asked to upgrade to the PRO plan, click **Maybe later**
96 |
97 |
98 |
99 | ### Setting Up UptimeRobot
100 | Now that you have an account and are logged in, you should see a page like the following:
101 |
102 |
103 |
104 | Click the **Add New Monitor** button. You'll see a popup like this:
105 |
106 |
107 |
108 | Enter the following, one at a time:
109 |
110 | * Monitor Type: **HTTP(s)**
111 | * Friendly Name: **tconnectsync**
112 | * Monitoring Interval: **every 30 minutes**
113 | * Monitor Timeout: **1 minute**
114 |
115 | Enter the following as the URL:
116 | ```
117 | https://MyNightscoutSite-tconnect.herokuapp.com/run?secret=YOUR_TCONNECTSYNC_HEROKU_SECRET_VALUE
118 | ```
119 |
120 | **MyNightscoutSite-tconnect** is the App Name you provided for tconnectsync-heroku.
121 |
122 | To get the value of **YOUR_TCONNECTSYNC_HEROKU_SECRET_VALUE**:
123 |
124 | * Go into your Heroku dashboard (https://dashboard.heroku.com) and select the app you created for tconnectsync:
125 |
126 |
127 |
128 | * Click on Settings > Reveal Config Vars.
129 |
130 |
131 |
132 | * Now find the row which has `TCONNECTSYNC_HEROKU_SECRET` displayed on the left,
133 | and copy the value contained in the right-most text box next to it.
134 | This is the value of the `TCONNECTSYNC_HEROKU_SECRET` environment variable.
135 |
136 | (If you'd like, you can change the value here to a different password.
137 | If so, then save settings and then restart the app in heroku via More > Restart All Dynos.)
138 |
139 |
140 | Your options should look similar to this within UptimeRobot (but with your actual tconnectsync-heroku URL and secret value):
141 |
142 |
143 |
144 | Click **Create Monitor**.
145 |
146 |
147 | You can optionally specify an _alert contact to notify_ if you would like to receive an email whenever tconnectsync-heroku experiences an error.
148 | If you don't specify one, you'll have to hit the button again.
149 |
150 |
151 | Now, every 30 minutes, tconnectsync will be triggered to synchronize your data between t:connect and Nightscout!
152 |
153 | ## Testing
154 |
155 | You can hit the following URL to verify that your tconnectsync options are
156 | specified correctly, and that both t:connect and Nightscout are accessible:
157 |
158 | ```
159 | https://MyNightscoutSite-tconnect.herokuapp.com/check_login?secret=YOUR_TCONNECTSYNC_HEROKU_SECRET_VALUE
160 | ```
161 |
162 | (Remember to replace `MyNightscoutSite-tconnect` and `YOUR_TCONNECTSYNC_HEROKU_SECRET_VALUE` with your own values like mentioned before.)
163 |
164 | There is a lot of debugging output on this page, so scroll to the very bottom.
165 | If it says **No API errors returned!** then you are all good to go!
166 |
167 | If the page doesn't load, you can also view the application-side error logs by going to the Heroku dashboard and clicking on **More > View logs**:
168 |
169 |
170 |
171 | **If you need help,** you can do one of the following:
172 | * Post in the **CGM in the Cloud** Facebook group
173 | * Open a GitHub Issue at https://github.com/jwoglom/tconnectsync/issues/new/choose with the `heroku` tag.
174 |
175 | When asking for help, please be ready to copy-and-paste the output of the `check_login` URL
176 | referenced above which contains diagnostic information which may help to solve your issue(s).
177 |
178 | **The remainder of the instructions below are for advanced use cases.**
179 | Once you've gotten to this point, wait 30 minutes to see if pump data starts appearing in Nightscout!
180 |
181 | ## Troubleshooting
182 |
183 | ### _I get a Heroku error page when clicking **Open App** from the Heroku dashboard_
184 |
185 | View the application-side error logs by going to the Heroku dashboard and clicking on **More > View logs**:
186 |
187 |
188 |
189 | Check if there are any errors about invalid configuration options.
190 | If the problem is something else, follow the **If you need help** instructions above.
191 |
192 | ### _The `check_login` page gives a `Received ApiException in ControlIQApi: ControlIQ API HTTP 404 response` error_
193 |
194 | Please verify that you've completed the following steps:
195 |
196 | * Have you created an account on the tconnect website?
197 | * With the tconnect credentials you've specified, can you:
198 | * Log in to the Android or iOS app, with your pump paired and sending data
199 | * Log in at https://tconnect.tandemdiabetes.com and see your pump information appear
200 |
201 | If tconnectsync still gives this error, try:
202 |
203 | * Downloading the [Windows or MacOS TConnect Uploader application](https://tconnect.tandemdiabetes.com/GettingStarted/Download.aspx) and plug your pump into your computer to upload your settings.
204 | * Resetting your password at https://tconnect.tandemdiabetes.com and setting it to the same password you use in the Android or iOS app
205 |
206 | If this doesn't help, follow the **If you need help** instructions above.
207 |
208 | ## Updating synchronization features
209 |
210 | By default, tconnectsync-heroku will upload the following data from your pump to Nightscout:
211 | * Basal events
212 | * Bolus events
213 |
214 | Tconnectsync supports additional so-called _synchronization features_ which enable it to send
215 | additional pump data into Nightscout. [You can see a list of these features here](https://github.com/jwoglom/tconnectsync#What-Gets-Synced).
216 |
217 | To set custom synchronization features, go into your Heroku Config Vars settings (Settings > Reveal Config Vars)
218 | and add a key and value with the following:
219 |
220 | * Key: `TCONNECTSYNC_HEROKU_FEATURES`
221 | * Value: a comma-separated list of features, without spaces. e.g., `BASAL,BOLUS,PUMP_EVENTS`
222 |
223 | If you don't set this value, tconnectsync-heroku will default to tconnectsync's default synchronization
224 | features (only basal and bolus data).
225 |
226 | ## Updating to a new version
227 |
228 | Updates are made frequently to tconnectsync to correct bugs or add new features.
229 | When some time has past after setting up tconnectsync-heroku and you want to ensure
230 | you are up to date, follow these instructions:
231 |
232 | To update your Heroku instance of tconnectsync, perform the following steps.
233 | This is a tconnectsync specific version of the "Deploy using Heroku Git"
234 | instructions which are present in the "Deploy" tab of your Heroku console.
235 |
236 | You should perform the following steps on a Linux or MacOS computer.
237 | If using Windows, install the [Windows Subsystem for Linux](https://ubuntu.com/wsl) and
238 | run these steps inside the Ubuntu Terminal.
239 |
240 | 1. [Install the Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli)
241 | 2. In a terminal, clone your Heroku repository and pull the latest version of tconnectsync-heroku:
242 | ```
243 | heroku git:clone -a YOUR_HEROKU_SITE_NAME
244 | cd YOUR_HEROKU_SITE_NAME
245 | git remote add upstream https://github.com/jwoglom/tconnectsync-heroku
246 | git pull -r upstream main
247 | ```
248 | 3. Deploy the updated app to Heroku:
249 | ```
250 | git push heroku main
251 | ```
252 |
253 | ## Alternate Configuration Using Background Tasks
254 | What follows is an alternate option, which may be easier to set up but may not work for all setups:
255 |
256 | Go into your Heroku Config Vars settings (Settings > Reveal Config Vars)
257 | and add a key and value with the following:
258 |
259 | * TCONNECTSYNC_HEROKU_INTERVAL_MINS - The interval at which a scheduled task should
260 | be performed for a synchronization. This will **only** function while the Flask
261 | application is running inside Heroku. (Heroku will occasionally shut down the app
262 | after some period of inactivity.)
263 |
264 | For example:
265 | * Key: `TCONNECTSYNC_HEROKU_INTERVAL_MINS`
266 | * Value: `30`
267 |
268 | When the Flask application is started, so long as it is running the tconnect
269 | synchronization task will run at the given interval.
270 |
271 | Note that **Heroku may shut down your dyno if it has not processed requests in
272 | 30 minutes**.
273 |
274 | You should ideally set up UptimeRobot to ping your application at the index URL
275 | (**not** the run endpoint, since that will cause the synchronization to happen
276 | both triggered by UptimeRobot and the background task daemon itself).
277 | This would reflect a URL such as:
278 |
279 | ```
280 | https://YOUR-TCONNECTSYNC-APP-NAME.herokuapp.com/
281 | ```
282 |
283 |
284 | [tconnectsync]: https://github.com/jwoglom/tconnectsync
285 | [tconnect-installation]: https://github.com/jwoglom/tconnectsync#installation
286 | [uptimerobot]: https://uptimerobot.com/
287 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "TConnectSync for Heroku",
3 | "description": "Flask wrapper for tconnectsync running in Heroku, which synchronizes t:connect data to Nightscout",
4 | "keywords": [
5 | "tconnect",
6 | "nightscout",
7 | "diabetes"
8 | ],
9 | "repository": "https://github.com/jwoglom/tconnectsync-heroku",
10 | "logo": "https://images.squarespace-cdn.com/content/v1/55f63267e4b0b1dfc5acf418/1460133565328-8JPQA577SKLI3YY9QB9N/image-asset.png?format=1000w",
11 | "success_url": "/",
12 | "env": {
13 | "TCONNECT_EMAIL": {
14 | "description": "Email address for tconnect",
15 | "value": ""
16 | },
17 | "TCONNECT_PASSWORD": {
18 | "description": "Password for tconnect",
19 | "value": ""
20 | },
21 | "PUMP_SERIAL_NUMBER": {
22 | "description": "Tandem t:slim X2 pump serial number. Visible in the t:connect web UI and in Settings > My Pump > Pump Info on the pump. Enter only the number, do not include '#'",
23 | "value": ""
24 | },
25 | "NS_URL": {
26 | "description": "URL of your Nightscout instance. Should be entered like https://mynightscouturl.herokuapp.com",
27 | "value": ""
28 | },
29 | "NS_SECRET": {
30 | "description": "Secret key (API_SECRET) of your Nightscout instance",
31 | "value": ""
32 | },
33 | "TIMEZONE_NAME": {
34 | "description": "Timezone of your phone and pump. In TZ database format: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones",
35 | "value": "America/New_York"
36 | },
37 | "TCONNECTSYNC_HEROKU_SECRET": {
38 | "description": "A secret key used to trigger a tconnectsync run and view additional info. This is auto-generated.",
39 | "generator": "secret"
40 | },
41 | "TCONNECTSYNC_HEROKU_INTERVAL_MINS": {
42 | "description": "How frequently to trigger tconnectsync on its own. Set to 0 or blank if tconnectsync should not be triggered in a background thread",
43 | "value": "0"
44 | }
45 | },
46 | "formation": {
47 | "web": {
48 | "quantity": 1,
49 | "size": "eco"
50 | }
51 | },
52 | "image": "heroku/python"
53 | }
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | """Wrapper for tconnectsync running in Heroku."""
2 |
3 | from variables import secret, interval_mins, tconnect_secret
4 | from utils import token_required, call, setup, get_time_args, run_update, as_text, parse_features
5 |
6 | from threading import Thread
7 | from flask import Flask, request
8 | from flask.templating import render_template
9 | from flask_apscheduler import APScheduler
10 | from flask_apscheduler.auth import HTTPBasicAuth
11 |
12 | from tconnectsync.check import check_login
13 | from tconnectsync import __version__ as tconnectsync_version
14 |
15 | version = '0.3.1'
16 | print("Launching tconnectsync-heroku %s" % str(version))
17 | print("Using tconnectsync %s" % str(tconnectsync_version))
18 |
19 | app = Flask(__name__)
20 | scheduler = APScheduler()
21 | scheduler.api_enabled = True
22 | scheduler.auth = HTTPBasicAuth()
23 | scheduler.init_app(app)
24 | scheduler.start()
25 |
26 | @scheduler.authenticate
27 | def authenticate(auth):
28 | if not secret:
29 | return False
30 |
31 | return auth["password"] == secret
32 |
33 | @app.route('/')
34 | def index_route():
35 | job = scheduler.get_job('update')
36 | return render_template('index.html',
37 | tconnectsyncheroku_version=version,
38 | tconnectsync_version=tconnectsync_version,
39 | job=job,
40 | interval_mins=interval_mins)
41 |
42 | """Runs the tconnectsync login check synchronously, returning the result."""
43 | @app.route('/check_login')
44 | @token_required
45 | def check_login_route():
46 | tconnect, _ = setup()
47 | time_start, time_end = get_time_args(request.values.get("days"))
48 | return as_text(*call(check_login, [tconnect, time_start, time_end, True]))
49 |
50 | """Runs tconnectsync synchronously, returning the result."""
51 | @app.route('/update')
52 | @token_required
53 | def update_route():
54 | days = request.values.get("days")
55 | pretend = bool(request.values.get("pretend"))
56 | features = parse_features(request.values.get("features"))
57 | return as_text(*run_update(days, pretend, features))
58 |
59 | """Runs tconnectsync in the background, does not return an error if one occurs."""
60 | @app.route('/run')
61 | @token_required
62 | def run_route():
63 | days = request.values.get("days")
64 | pretend = bool(request.values.get("pretend"))
65 | features = parse_features(request.values.get("features"))
66 | Thread(target=run_update, kwargs={'days': days, 'pretend': pretend, 'features': features}).start()
67 | return 'Triggered job'
68 |
69 | if interval_mins:
70 | print("Creating scheduled task for every", interval_mins, "minutes")
71 | @scheduler.task('interval', id='update', seconds=interval_mins * 60, misfire_grace_time=300)
72 | def scheduled_update_task():
73 | print('Running update scheduler task')
74 | run_update(1, False, parse_features(None))
75 | print('Finished update scheduler task')
76 |
77 | if __name__ == '__main__':
78 | import os
79 | LISTEN_HOST = os.getenv('LISTEN_HOST', '0.0.0.0')
80 | LISTEN_PORT = int(os.getenv('LISTEN_PORT', '5000'))
81 | app.run(LISTEN_HOST, LISTEN_PORT)
82 |
--------------------------------------------------------------------------------
/runtime.txt:
--------------------------------------------------------------------------------
1 | python-3.9.13
2 |
--------------------------------------------------------------------------------
/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
The tconnectsync-heroku server is running
9 |Tconnectsync will automatically run approximately every {{ interval_mins }} min so long as the Flask application is running.
16 |It will run next at {{ job.next_run_time }}.
17 | {% else %} 18 |Tconnectsync must be triggered manually by an external service (e.g. UptimeRobot).
19 | {% endif %} 20 | 21 |22 |
Need help? Open a new issue on GitHub and add the "setup help" tag.
23 | 24 | 25 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from variables import secret, interval_mins, tconnect_secret, default_features 2 | 3 | import io 4 | import traceback 5 | import datetime 6 | 7 | from contextlib import redirect_stdout, redirect_stderr 8 | from functools import wraps 9 | from flask import request, make_response 10 | 11 | from tconnectsync.api import TConnectApi 12 | from tconnectsync.nightscout import NightscoutApi 13 | from tconnectsync.sync.tandemsource.heroku_helpers import run_oneshot 14 | from tconnectsync.features import DEFAULT_FEATURES, ALL_FEATURES 15 | 16 | def call(fn, args, **kwargs): 17 | print('call args:', args) 18 | s = io.StringIO() 19 | code = 200 20 | with redirect_stdout(s), redirect_stderr(s): 21 | try: 22 | fn(*args, **kwargs) 23 | except Exception: 24 | traceback.print_exc(file=s) 25 | code = 500 26 | 27 | out = s.getvalue() 28 | print('call output:', out) 29 | 30 | return out, code 31 | 32 | def as_text(*args): 33 | resp = make_response(*args) 34 | resp.mimetype = 'text/plain' 35 | return resp 36 | 37 | def token_required(f): 38 | @wraps(f) 39 | def decorated_function(*args, **kwargs): 40 | if not secret: 41 | return "secret is not configured in the settings, so this page is inaccessible", 403 42 | if request.values.get("secret") is None: 43 | return "secret not provided as a GET or POST argument", 403 44 | if request.values.get("secret") != secret: 45 | return "secret is invalid", 403 46 | return f(*args, **kwargs) 47 | return decorated_function 48 | 49 | def setup(): 50 | tconnect = TConnectApi(tconnect_secret.TCONNECT_EMAIL, tconnect_secret.TCONNECT_PASSWORD) 51 | nightscout = NightscoutApi(tconnect_secret.NS_URL, tconnect_secret.NS_SECRET) 52 | 53 | return tconnect, nightscout 54 | 55 | def get_time_args(days): 56 | if days: 57 | days = int(days) 58 | else: 59 | days = 1 60 | 61 | time_end = datetime.datetime.now() 62 | time_start = time_end - datetime.timedelta(days=days) 63 | 64 | return time_start, time_end 65 | 66 | def run_update(days, pretend, features): 67 | tconnect, nightscout = setup() 68 | time_start, time_end = get_time_args(days) 69 | 70 | if features is None: 71 | features = DEFAULT_FEATURES 72 | 73 | out, code = call(run_oneshot, [tconnect, nightscout, pretend, features, tconnect_secret, time_start, time_end]) 74 | 75 | print('Completed with', code) 76 | print(out) 77 | 78 | return out, code 79 | 80 | # In order, uses the features (in comma-delimited format) that are: 81 | # - specified in the query (from the given argument) 82 | # - specified as TCONNECTSYNC_HEROKU_FEATURES to tconnectsync-heroku 83 | # The default value is the tconnectsync DEFAULT_FEATURES 84 | def parse_features(features): 85 | ret = default_features.split(",") 86 | if features is not None and len(features) > 0: 87 | ret = features.split(",") 88 | 89 | ret = [i.strip() for i in ret] 90 | for f in ret: 91 | if f not in ALL_FEATURES: 92 | raise RuntimeError("The feature '%s' was not recognized by tconnectsync. It is not one of: %s" % (f, str(ALL_FEATURES))) 93 | return ret 94 | -------------------------------------------------------------------------------- /variables.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | secret = os.environ.get('TCONNECTSYNC_HEROKU_SECRET') 4 | if not secret: 5 | print("The TCONNECTSYNC_HEROKU_SECRET environment variable is undefined,") 6 | print("so the API endpoints will not be accessible.") 7 | secret = None 8 | 9 | interval_mins = os.environ.get('TCONNECTSYNC_HEROKU_INTERVAL_MINS') 10 | if interval_mins: 11 | interval_mins = int(interval_mins) 12 | if interval_mins < 1: 13 | interval_mins = None 14 | 15 | if not interval_mins: 16 | print("The TCONNECTSYNC_HEROKU_INTERVAL_MINS environment variable is undefined,") 17 | print("so tconnectsync will not run automatically") 18 | interval_mins = None 19 | 20 | try: 21 | from tconnectsync import secret as tconnect_secret 22 | except Exception as e: 23 | print("Tconnect environment variables are undefined.") 24 | print("Please follow the setup instructions at:") 25 | print("https://github.com/jwoglom/tconnectsync-heroku") 26 | raise e 27 | 28 | from tconnectsync.features import DEFAULT_FEATURES as tconnectsync_default_features 29 | 30 | default_features = os.environ.get('TCONNECTSYNC_HEROKU_FEATURES') 31 | if not default_features: 32 | default_features = ",".join(tconnectsync_default_features) 33 | print("The TCONNECTSYNC_HEROKU_FEATURES environment variable is undefined,") 34 | print("so the following default tconnectsync features will be used: %s" % default_features) 35 | --------------------------------------------------------------------------------