diff --git a/.gitignore b/.gitignore index bdd4074d7d98ff4c226296bfaf9fd16a18e1283d..55debd42d1b4c4df14b7afa9d7a98620fe2f45e2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ backend/secfit/.vscode/ backend/secfit/*/migrations/__pycache__/ backend/secfit/*/__pycache__/ backend/secfit/db.sqlite3 + +.idea/ +venv/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..2d1d33bf15767e7815ec4f47e13a3068cfb29a1f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,44 @@ +variables: + HEROKU_APP_NAME: tdt4242-base + HEROKU_REGISTRY_IMAGE: registry.heroku.com/${HEROKU_APP_NAME}/web +stages: + - test + - build + - release + +test: + image: python:3 + stage: test + script: + # this configures Django application to use attached postgres database that is run on `postgres` host + - cd backend/secfit + - apt-get update -qy + - pip install -r requirements.txt + - python manage.py test + +build_image: + only: + - master + image: registry.gitlab.com/majorhayden/container-buildah + stage: build + variables: + STORAGE_DRIVER: "vfs" + BUILDAH_FORMAT: "docker" + before_script: + - dnf install -y nodejs + - curl https://cli-assets.heroku.com/install.sh | sh + - sed -i '/^mountopt =.*/d' /etc/containers/storage.conf + script: + - buildah bud --iidfile iidfile -t rust-python-demo:$CI_COMMIT_SHORT_SHA . + - buildah push --creds=_:$(heroku auth:token) $(cat iidfile) registry.heroku.com/tdt4242-base/web + +release: + only: + - master + image: node:10.17-alpine + stage: release + before_script: + - apk add curl bash + - curl https://cli-assets.heroku.com/install.sh | sh + script: + - heroku container:release -a tdt4242-base web \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000000000000000000000000000000000000..591ee5f685cc3821282287ac6c07be789d6dc7d1 --- /dev/null +++ b/Pipfile @@ -0,0 +1,43 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +requests = "==2.24.0" +asgiref = "==3.2.10" +astroid = "==2.4.2" +certifi = "==2020.6.20" +chardet = "==3.0.4" +colorama = "==0.4.3" +dj-database-url = "==0.5.0" +django-cleanup = "==5.0.0" +django-cors-headers = "==3.4.0" +djangorestframework = "==3.11.1" +djangorestframework-simplejwt = "==4.6.0" +gunicorn = "==20.0.4" +httpie = "==2.2.0" +idna = "==2.10" +isort = "==4.3.21" +lazy-object-proxy = "==1.4.3" +mccabe = "==0.6.1" +psycopg2-binary = "*" +pylint = "==2.5.3" +pylint-django = "==2.3.0" +pylint-plugin-utils = "==0.6" +pytz = "==2020.1" +rope = "==0.17.0" +six = "==1.15.0" +sqlparse = "==0.3.1" +toml = "==0.10.1" +urllib3 = "==1.25.10" +whitenoise = "==5.2.0" +wrapt = "==1.12.1" +Django = "==3.1" +Pygments = "==2.6.1" +PyJWT = "==1.7.1" + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000000000000000000000000000000000000..2c073933338816e2d57b6e5bfd0ad619fd8e8761 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,327 @@ +{ + "_meta": { + "hash": { + "sha256": "f8792657ccce48034fdaeda633380958787ebae652bda60c7f24c8f89d53b20e" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "asgiref": { + "hashes": [ + "sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a", + "sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed" + ], + "index": "pypi", + "version": "==3.2.10" + }, + "astroid": { + "hashes": [ + "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703", + "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386" + ], + "index": "pypi", + "version": "==2.4.2" + }, + "certifi": { + "hashes": [ + "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", + "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" + ], + "index": "pypi", + "version": "==2020.6.20" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "index": "pypi", + "version": "==3.0.4" + }, + "colorama": { + "hashes": [ + "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff", + "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1" + ], + "index": "pypi", + "version": "==0.4.3" + }, + "dj-database-url": { + "hashes": [ + "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163", + "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9" + ], + "index": "pypi", + "version": "==0.5.0" + }, + "django": { + "hashes": [ + "sha256:1a63f5bb6ff4d7c42f62a519edc2adbb37f9b78068a5a862beff858b68e3dc8b", + "sha256:2d390268a13c655c97e0e2ede9d117007996db692c1bb93eabebd4fb7ea7012b" + ], + "index": "pypi", + "version": "==3.1" + }, + "django-cleanup": { + "hashes": [ + "sha256:84f0c0e0a74545adae4c944a76ccf8fb0c195dddccf3b7195c59267abb7763dd", + "sha256:de5948e74e00fc74d19bf15e062477b45090ba467587f45b2459eae8f97bc4f4" + ], + "index": "pypi", + "version": "==5.0.0" + }, + "django-cors-headers": { + "hashes": [ + "sha256:5240062ef0b16668ce8a5f43324c388d65f5439e1a30e22c38684d5ddaff0d15", + "sha256:f5218f2f0bb1210563ff87687afbf10786e080d8494a248e705507ebd92d7153" + ], + "index": "pypi", + "version": "==3.4.0" + }, + "djangorestframework": { + "hashes": [ + "sha256:6dd02d5a4bd2516fb93f80360673bf540c3b6641fec8766b1da2870a5aa00b32", + "sha256:8b1ac62c581dbc5799b03e535854b92fc4053ecfe74bad3f9c05782063d4196b" + ], + "index": "pypi", + "version": "==3.11.1" + }, + "djangorestframework-simplejwt": { + "hashes": [ + "sha256:7adc913ba0d2ed7f46e0b9bf6e86f9bd9248f1c4201722b732b8213e0ea66f9f", + "sha256:bd587700b6ab34a6c6b12d426cce4fa580d57ef1952ad4ba3b79707784619ed3" + ], + "index": "pypi", + "version": "==4.6.0" + }, + "gunicorn": { + "hashes": [ + "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626", + "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c" + ], + "index": "pypi", + "version": "==20.0.4" + }, + "httpie": { + "hashes": [ + "sha256:31ac28088ee6a0b6f3ba7a53379000c4d1910c1708c9ff768f84b111c14405a0", + "sha256:aab111d347a3059ba507aa9339c621e5cae6658cc96f365cd6a32ae0fb6ad8aa" + ], + "index": "pypi", + "version": "==2.2.0" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "index": "pypi", + "version": "==2.10" + }, + "isort": { + "hashes": [ + "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", + "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" + ], + "index": "pypi", + "version": "==4.3.21" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", + "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", + "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", + "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", + "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", + "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", + "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", + "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", + "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", + "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", + "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", + "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", + "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", + "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", + "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", + "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", + "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", + "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", + "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", + "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", + "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" + ], + "index": "pypi", + "version": "==1.4.3" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "index": "pypi", + "version": "==0.6.1" + }, + "psycopg2-binary": { + "hashes": [ + "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c", + "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67", + "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0", + "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6", + "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db", + "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94", + "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52", + "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056", + "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b", + "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd", + "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550", + "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679", + "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83", + "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77", + "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2", + "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77", + "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2", + "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd", + "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859", + "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1", + "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25", + "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152", + "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf", + "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f", + "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729", + "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71", + "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66", + "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4", + "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449", + "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da", + "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a", + "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c", + "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb", + "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4", + "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5" + ], + "index": "pypi", + "version": "==2.8.6" + }, + "pygments": { + "hashes": [ + "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", + "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" + ], + "index": "pypi", + "version": "==2.6.1" + }, + "pyjwt": { + "hashes": [ + "sha256:5c6eca3c2940464d106b99ba83b00c6add741c9becaec087fb7ccdefea71350e", + "sha256:8d59a976fb773f3e6a39c85636357c4f0e242707394cadadd9814f5cbaa20e96" + ], + "index": "pypi", + "version": "==1.7.1" + }, + "pylint": { + "hashes": [ + "sha256:7dd78437f2d8d019717dbf287772d0b2dbdfd13fc016aa7faa08d67bccc46adc", + "sha256:d0ece7d223fe422088b0e8f13fa0a1e8eb745ebffcb8ed53d3e95394b6101a1c" + ], + "index": "pypi", + "version": "==2.5.3" + }, + "pylint-django": { + "hashes": [ + "sha256:770e0c55fb054c6378e1e8bb3fe22c7032a2c38ba1d1f454206ee9c6591822d7", + "sha256:b8dcb6006ae9fa911810aba3bec047b9410b7d528f89d5aca2506b03c9235a49" + ], + "index": "pypi", + "version": "==2.3.0" + }, + "pylint-plugin-utils": { + "hashes": [ + "sha256:2f30510e1c46edf268d3a195b2849bd98a1b9433229bb2ba63b8d776e1fc4d0a", + "sha256:57625dcca20140f43731311cd8fd879318bf45a8b0fd17020717a8781714a25a" + ], + "index": "pypi", + "version": "==0.6" + }, + "pytz": { + "hashes": [ + "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", + "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" + ], + "index": "pypi", + "version": "==2020.1" + }, + "requests": { + "hashes": [ + "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", + "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" + ], + "index": "pypi", + "version": "==2.24.0" + }, + "rope": { + "hashes": [ + "sha256:658ad6705f43dcf3d6df379da9486529cf30e02d9ea14c5682aa80eb33b649e1" + ], + "index": "pypi", + "version": "==0.17.0" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "index": "pypi", + "version": "==1.15.0" + }, + "sqlparse": { + "hashes": [ + "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e", + "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548" + ], + "index": "pypi", + "version": "==0.3.1" + }, + "toml": { + "hashes": [ + "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", + "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" + ], + "index": "pypi", + "version": "==0.10.1" + }, + "urllib3": { + "hashes": [ + "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a", + "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461" + ], + "index": "pypi", + "version": "==1.25.10" + }, + "whitenoise": { + "hashes": [ + "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7", + "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d" + ], + "index": "pypi", + "version": "==5.2.0" + }, + "wrapt": { + "hashes": [ + "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7" + ], + "index": "pypi", + "version": "==1.12.1" + } + }, + "develop": {} +} diff --git a/backend/secfit/requirements.txt b/backend/secfit/requirements.txt index 9feb375bde1e8fb7befe6c102dd29beeee7c6940..bb4d37fd1a49afc92c9df6f535c4956fdebb805b 100644 Binary files a/backend/secfit/requirements.txt and b/backend/secfit/requirements.txt differ diff --git a/backend/secfit/secfit/settings.py b/backend/secfit/secfit/settings.py index 92336536fbb75beeade6bfc8e60eb393531832c9..6f71ccf79bdf0bf29d89a1381795039cd5c480e0 100644 --- a/backend/secfit/secfit/settings.py +++ b/backend/secfit/secfit/settings.py @@ -43,6 +43,7 @@ ALLOWED_HOSTS = [ "10." + groupid + ".0.4", "molde.idi.ntnu.no", "10.0.2.2", + "tdt4242-base.herokuapp.com" ] # Application definition diff --git a/frontend/www/scripts/workout.js b/frontend/www/scripts/workout.js index 94eddb777de4dba4cc24b4ef447430a989b0c935..9cb67115417e25a1117118edab54121d86f7aed0 100644 --- a/frontend/www/scripts/workout.js +++ b/frontend/www/scripts/workout.js @@ -2,9 +2,10 @@ let cancelWorkoutButton; let okWorkoutButton; let deleteWorkoutButton; let editWorkoutButton; +let exportWorkoutButton; let postCommentButton; -async function retrieveWorkout(id) { +async function retrieveWorkout(id) { let workoutData = null; let response = await sendRequest("GET", `${HOST}/api/workouts/${id}/`); if (!response.ok) { @@ -57,11 +58,11 @@ async function retrieveWorkout(id) { let exerciseTypeLabel = divExerciseContainer.querySelector('.exercise-type'); exerciseTypeLabel.for = `inputExerciseType${i}`; - - let exerciseTypeSelect = divExerciseContainer.querySelector("select"); + + let exerciseTypeSelect = divExerciseContainer.querySelector("select"); exerciseTypeSelect.id = `inputExerciseType${i}`; exerciseTypeSelect.disabled = true; - + let splitUrl = workoutData.exercise_instances[i].exercise.split("/"); let currentExerciseTypeId = splitUrl[splitUrl.length - 2]; let currentExerciseType = ""; @@ -75,7 +76,7 @@ async function retrieveWorkout(id) { option.innerText = exerciseTypes.results[j].name; exerciseTypeSelect.append(option); } - + exerciseTypeSelect.value = currentExerciseType.id; let exerciseSetLabel = divExerciseContainer.querySelector('.exercise-sets'); @@ -99,7 +100,7 @@ async function retrieveWorkout(id) { exercisesDiv.appendChild(divExerciseContainer); } } - return workoutData; + return workoutData; } function handleCancelDuringWorkoutEdit() { @@ -109,11 +110,12 @@ function handleCancelDuringWorkoutEdit() { function handleEditWorkoutButtonClick() { let addExerciseButton = document.querySelector("#btn-add-exercise"); let removeExerciseButton = document.querySelector("#btn-remove-exercise"); - + setReadOnly(false, "#form-workout"); document.querySelector("#inputOwner").readOnly = true; // owner field should still be readonly editWorkoutButton.className += " hide"; + exportWorkoutButton.className += " hide"; okWorkoutButton.className = okWorkoutButton.className.replace(" hide", ""); cancelWorkoutButton.className = cancelWorkoutButton.className.replace(" hide", ""); deleteWorkoutButton.className = deleteWorkoutButton.className.replace(" hide", ""); @@ -124,6 +126,91 @@ function handleEditWorkoutButtonClick() { } +//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 +function handleExportToCalendarClick(workoutData) { + + const headers = { + subject: "Subject", + startDate: "Start date", + startTime: "Start time", + description: "Description" + } + + const dataFormatted = [] + + const startTime = new Date(workoutData.date).toLocaleTimeString("en-us") + const startDate = new Date(workoutData.date).toLocaleString('en-us', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }).replace(/(\d+)\/(\d+)\/(\d+)/, '$1/$2/$3') + + + dataFormatted.push({ + subject: workoutData.name, + startDate: startDate, + startTime: startTime, + description: workoutData.notes + }) + + + console.log(dataFormatted) + + exportCSVFile(headers, dataFormatted, "event") +} + +//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 +function convertToCSV(objArray) { + var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray; + var str = ''; + + for (var i = 0; i < array.length; i++) { + var line = ''; + for (var index in array[i]) { + if (line != '') line += ',' + + line += array[i][index]; + } + + str += line + '\r\n'; + } + + return str; +} + +//Taken from github: https://gist.github.com/dannypule/48418b4cd8223104c6c92e3016fc0f61 +function exportCSVFile(headers, items, fileTitle) { + + console.log(items, headers) + if (headers) { + items.unshift(headers); + } + + // Convert Object to JSON + var jsonObject = JSON.stringify(items); + + var csv = this.convertToCSV(jsonObject); + + var exportedFilenmae = fileTitle + '.csv' || 'export.csv'; + + var blob = new Blob([csv], {type: 'text/csv;charset=utf-8;'}); + if (navigator.msSaveBlob) { // IE 10+ + navigator.msSaveBlob(blob, exportedFilenmae); + } else { + var link = document.createElement("a"); + if (link.download !== undefined) { // feature detection + // Browsers that support HTML5 download attribute + var url = URL.createObjectURL(blob); + link.setAttribute("href", url); + link.setAttribute("download", exportedFilenmae); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + } +} + async function deleteWorkout(id) { let response = await sendRequest("DELETE", `${HOST}/api/workouts/${id}/`); if (!response.ok) { @@ -208,7 +295,7 @@ async function createBlankExercise() { let exerciseTemplate = document.querySelector("#template-exercise"); let divExerciseContainer = exerciseTemplate.content.firstElementChild.cloneNode(true); let exerciseTypeSelect = divExerciseContainer.querySelector("select"); - + for (let i = 0; i < exerciseTypes.count; i++) { let option = document.createElement("option"); option.value = exerciseTypes.results[i].id; @@ -218,7 +305,7 @@ async function createBlankExercise() { let currentExerciseType = exerciseTypes.results[0]; exerciseTypeSelect.value = currentExerciseType.name; - + let divExercises = document.querySelector("#div-exercises"); divExercises.appendChild(divExerciseContainer); } @@ -251,7 +338,7 @@ function addComment(author, text, date, append) { dateSpan.appendChild(smallText); commentBody.appendChild(dateSpan); - + let strong = document.createElement("strong"); strong.className = "text-success"; strong.innerText = author; @@ -309,6 +396,7 @@ window.addEventListener("DOMContentLoaded", async () => { okWorkoutButton = document.querySelector("#btn-ok-workout"); deleteWorkoutButton = document.querySelector("#btn-delete-workout"); editWorkoutButton = document.querySelector("#btn-edit-workout"); + exportWorkoutButton = document.querySelector("#btn-export-workout"); let postCommentButton = document.querySelector("#post-comment"); let divCommentRow = document.querySelector("#div-comment-row"); let buttonAddExercise = document.querySelector("#btn-add-exercise"); @@ -327,7 +415,9 @@ window.addEventListener("DOMContentLoaded", async () => { if (workoutData["owner"] == currentUser.url) { editWorkoutButton.classList.remove("hide"); + exportWorkoutButton.classList.remove("hide"); editWorkoutButton.addEventListener("click", handleEditWorkoutButtonClick); + exportWorkoutButton.addEventListener("click", ((workoutData) => handleExportToCalendarClick(workoutData)).bind(undefined, workoutData)); deleteWorkoutButton.addEventListener("click", (async (id) => await deleteWorkout(id)).bind(undefined, id)); okWorkoutButton.addEventListener("click", (async (id) => await updateWorkout(id)).bind(undefined, id)); postCommentButton.addEventListener("click", (async (id) => await createComment(id)).bind(undefined, id)); diff --git a/frontend/www/styles/style.css b/frontend/www/styles/style.css index 066705ce965bb162c61ebaf65ff77b9a0824eaf3..89160462b2a3b07889aa1818320aeda482d845d9 100644 --- a/frontend/www/styles/style.css +++ b/frontend/www/styles/style.css @@ -62,3 +62,8 @@ .link-block { display: block; } + +.btn-green { + background-color: #256d27; + color: #fff; +} diff --git a/frontend/www/workout.html b/frontend/www/workout.html index 73747232adaea6ef4944cdd0fbec8693ca6b4a76..2e5d881a2103c5117d061618f44465d4486f7e7f 100644 --- a/frontend/www/workout.html +++ b/frontend/www/workout.html @@ -60,6 +60,7 @@
+
diff --git a/release.sh b/release.sh new file mode 100644 index 0000000000000000000000000000000000000000..19ebb5a890dd92982ee6f3826240dcb7e380630c --- /dev/null +++ b/release.sh @@ -0,0 +1,11 @@ +#!/bin/sh + + +IMAGE_ID=$(docker inspect ${HEROKU_REGISTRY_IMAGE} --format={{.Id}}) +PAYLOAD='{"updates": [{"type": "web", "docker_image": "'"$IMAGE_ID"'"}]}' + +curl -n -X PATCH https://api.heroku.com/apps/$HEROKU_APP_NAME/formation \ + -d "${PAYLOAD}" \ + -H "Content-Type: application/json" \ + -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \ + -H "Authorization: Bearer ${HEROKU_AUTH_TOKEN}" \ No newline at end of file