diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a821827 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3-alpine +ENV PYTHONUNBUFFERED 1 +RUN apk update +RUN apk add --no-cache tini bash unrar dcron postgresql-dev +RUN mkdir /src +WORKDIR /src +ADD requirements.txt /src/ +RUN apk add --no-cache --virtual .build-deps mariadb-dev build-base \ + && pipenv install --system + && apk add --virtual .runtime-deps mariadb-connector-c-dev mariadb-connector-c \ + && apk del .build-deps +ADD . /src/ +RUN cat /src/cbreader/crontab >> /etc/crontabs/root \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..980c72b --- /dev/null +++ b/Pipfile @@ -0,0 +1,16 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +ujson = "*" +django-silk = "*" +django-recaptcha2 = "*" +Django = "*" +gunicorn = "*" + +[requires] +python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..ebaa771 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,189 @@ +{ + "_meta": { + "hash": { + "sha256": "484cc2c0943d2a720e2e0d2c4a722378da9e7a4554695c84e6e67a50f4fb8914" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "autopep8": { + "hashes": [ + "sha256:33d2b5325b7e1afb4240814fe982eea3a92ebea712869bfd08b3c0393404248c" + ], + "version": "==1.4.3" + }, + "certifi": { + "hashes": [ + "sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7", + "sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033" + ], + "version": "==2018.11.29" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "django": { + "hashes": [ + "sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade", + "sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963" + ], + "index": "pypi", + "version": "==2.1.7" + }, + "django-recaptcha2": { + "hashes": [ + "sha256:9ea90db0cec502741be1066c09ec1b8e02a73162a319a042e78e67c4605087af", + "sha256:c0b43851b05c6bf6ebb5ecc890c13ccedacd9bb33d64b4291c74dd6fcbc89366" + ], + "index": "pypi", + "version": "==1.4.1" + }, + "django-silk": { + "hashes": [ + "sha256:ab6b7151a54eaa14d4fc77a58fd75e7c0c8bd60d29c87e55575845a304b0c0eb", + "sha256:bce0e35d2a6ec3688a0c062c6964695beef4a452be48085f2c1e25f685652d9d" + ], + "index": "pypi", + "version": "==3.0.1" + }, + "gprof2dot": { + "hashes": [ + "sha256:48c1e168c28b8a8eb23bf30fda78fe2ef218269a41505341ec27c27083e47cf4" + ], + "version": "==2016.10.13" + }, + "gunicorn": { + "hashes": [ + "sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471", + "sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3" + ], + "index": "pypi", + "version": "==19.9.0" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "markupsafe": { + "hashes": [ + "sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", + "sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", + "sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", + "sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", + "sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", + "sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", + "sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", + "sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", + "sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", + "sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", + "sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", + "sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", + "sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", + "sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", + "sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", + "sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", + "sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", + "sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", + "sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", + "sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", + "sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", + "sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", + "sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", + "sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", + "sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", + "sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", + "sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", + "sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1" + ], + "version": "==1.1.0" + }, + "pycodestyle": { + "hashes": [ + "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", + "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" + ], + "version": "==2.5.0" + }, + "pygments": { + "hashes": [ + "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", + "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d" + ], + "version": "==2.3.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + ], + "version": "==2.8.0" + }, + "pytz": { + "hashes": [ + "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", + "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" + ], + "version": "==2018.9" + }, + "requests": { + "hashes": [ + "sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", + "sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" + ], + "version": "==2.21.0" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "sqlparse": { + "hashes": [ + "sha256:ce028444cfab83be538752a2ffdb56bc417b7784ff35bb9a3062413717807dec", + "sha256:d9cf190f51cbb26da0412247dfe4fb5f4098edb73db84e02f9fc21fdca31fed4" + ], + "version": "==0.2.4" + }, + "ujson": { + "hashes": [ + "sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86" + ], + "index": "pypi", + "version": "==1.35" + }, + "urllib3": { + "hashes": [ + "sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39", + "sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22" + ], + "version": "==1.24.1" + } + }, + "develop": {} +} diff --git a/comic/models.py b/comic/models.py index d1b175d..69b611d 100644 --- a/comic/models.py +++ b/comic/models.py @@ -78,6 +78,10 @@ class ComicBook(models.Model): def __str__(self): return self.file_name + @property + def selector_string(self): + return urlsafe_base64_encode(self.selector.bytes).decode() + def get_image(self, page): base_dir = Setting.objects.get(name='BASE_DIR').value if self.directory: @@ -329,7 +333,11 @@ class ComicStatus(models.Model): return self.last_read_page def __str__(self): - return 'C:{0} P:{1}'.format(self.comic.file_name, self.last_read_page) + return self.__repr__() + + def __repr__(self): + return f'', + 'label': '
Unread
', + 'name': 'test1.rar', + 'selector': urlsafe_base64_encode(book.selector.bytes).decode(), + 'type': 'book', + 'url': f'/comic/read/' + f'{urlsafe_base64_encode(book.selector.bytes).decode()}/0/'}], + 'recordsFiltered': 1, + 'recordsTotal': 4}) + req_data['search[value]'] = '' + req_data['order[0][dir]'] = 3 + response = c.post('/comic/recent/json/', req_data) + + self.assertListEqual([x['name'] for x in json.loads(response.content)['data']], + ['test1.rar', 'test2.rar', 'test4.rar', 'test3.rar']) + + req_data['order[0][dir]'] = 2 + response = c.post('/comic/recent/json/', req_data) + self.assertListEqual([x['name'] for x in json.loads(response.content)['data']], + ['test1.rar', 'test2.rar', 'test4.rar', 'test3.rar']) + + def test_comic_edit(self): + c = Client() + book: ComicBook = ComicBook.objects.first() + user = User.objects.get(username='test') + + response = c.get('/comic/edit/') + self.assertEqual(response.status_code, 302) + c.login(username='test', password='test') + + response = c.get('/comic/edit/') + self.assertEqual(response.status_code, 405) + + req_data = {"comic_list_length": 10, "func": "unread", "selected": book.selector_string} + response = c.post('/comic/edit/', req_data) + self.assertEqual(response.status_code, 200) + + status = ComicStatus.objects.get(comic=book, user=user) + self.assertEqual(status.last_read_page, 0) + self.assertTrue(status.unread) + self.assertFalse(status.finished) + + req_data['func'] = 'read' + response = c.post('/comic/edit/', req_data) + self.assertEqual(response.status_code, 200) + status.refresh_from_db() + self.assertEqual(status.last_read_page, 3) + self.assertFalse(status.unread) + self.assertTrue(status.finished) + + req_data['func'] = 'choose' + response = c.post('/comic/edit/', req_data) + self.assertEqual(response.status_code, 200) + status.refresh_from_db() + self.assertEqual(status.last_read_page, 3) + self.assertFalse(status.unread) + self.assertTrue(status.finished) + + del req_data['selected'] + response = c.post('/comic/edit/', req_data) + self.assertEqual(response.status_code, 200) + + def test_account_page(self): + c = Client() + user = User.objects.get(username='test') + + response = c.get('/comic/account/') + self.assertEqual(response.status_code, 302) + + c.login(username='test', password='test') + + response = c.get('/comic/account/') + self.assertEqual(response.status_code, 200) + + response = c.post('/comic/account/') diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1e76e22..0000000 --- a/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -ujson -django -django-silk -django-recaptcha2 \ No newline at end of file