Skip to content

Commit 7efd9dc

Browse files
olmokramerlibre-man
authored andcommitted
WIP: Initial implementation (#2)
1 parent 9fb8b4a commit 7efd9dc

15 files changed

Lines changed: 1619 additions & 75 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
codegra-fs
1+
/env/
2+
/__pycache__/

.travis.yml

Lines changed: 55 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,67 @@
1-
language: c
2-
sudo: false
1+
language: python
2+
sudo: required
33
notifications:
44
email: false
55

6-
script: make
6+
services:
7+
- postgresql
78

89
branches:
910
only:
10-
- master
11+
- master
1112

12-
addons:
13-
apt:
14-
packages: &common_deps
15-
- pkg-config
16-
- libfuse-dev
17-
- curl
13+
cache:
14+
pip: true
15+
apt: true
1816

1917
matrix:
2018
include:
21-
- compiler: gcc-5
22-
addons:
23-
apt:
24-
sources:
25-
- ubuntu-toolchain-r-test
26-
packages:
27-
- gcc-5
28-
- *common_deps
29-
- compiler: gcc-6
30-
addons:
31-
apt:
32-
sources:
33-
- ubuntu-toolchain-r-test
34-
packages:
35-
- gcc-6
36-
- *common_deps
37-
- compiler: gcc-7
38-
addons:
39-
apt:
40-
sources:
41-
- ubuntu-toolchain-r-test
42-
packages:
43-
- gcc-7
44-
- *common_deps
45-
- compiler: clang-3.9
46-
addons:
47-
apt:
48-
sources:
49-
- llvm-toolchain-trusty-3.9
50-
packages:
51-
- clang-3.9
52-
- *common_deps
53-
- compiler: clang-4.0
54-
addons:
55-
apt:
56-
sources:
57-
- llvm-toolchain-trusty-4.0
58-
packages:
59-
- clang-4.0
60-
- *common_deps
61-
- compiler: clang-5.0
62-
addons:
63-
apt:
64-
sources:
65-
- llvm-toolchain-trusty
66-
packages:
67-
- clang-5.0
68-
- *common_deps
19+
- python: "3.6-dev" # 3.6 development branch
20+
21+
addons:
22+
apt:
23+
packages:
24+
- jq
25+
- flex
6926

7027
before_install:
71-
- eval "${MATRIX_EVAL}"
28+
- sudo apt-get install -qq pkg-config fuse
29+
- sudo modprobe fuse
30+
- sudo chmod 666 /dev/fuse
31+
- sudo chown root:$USER /etc/fuse.conf
32+
33+
install:
34+
- pip install -U wheel
35+
- pip install coveralls pytest-cov
36+
- pip install codecov
37+
- pip install -r requirements.txt
38+
- git clone https://github.com/CodeGra-de/CodeGra.de.git backend
39+
- pip install -r backend/requirements.txt
40+
41+
before_script:
42+
- pwd
43+
- ls
44+
- echo -e '[Back-end]\nsqlalchemy_database_uri = postgresql:///travis_ci_test\nDEBUG = true' > backend/config.ini
45+
- mkdir -p backend/uploads
46+
- mkdir -p backend/mirror_uploads
47+
- python --version
48+
- export PYTHONPATH="$PYTHONPATH:$(pwd)"
49+
- export DEBUG=true
50+
- psql -c 'create database travis_ci_test;' -U postgres
51+
- export PYTHON=python
52+
- cd backend
53+
- make db_upgrade
54+
- make test_data
55+
- make start_dev_server > ../server.log 2>&1 &
56+
- cd ..
57+
- sleep 6
58+
59+
script:
60+
- make test
61+
62+
after_script:
63+
cat server.log
64+
65+
after_success:
66+
- coveralls
67+
- codecov

Makefile

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
CFLAGS = -std=c11 -Werror -Wall -Wextra -pedantic $(shell pkg-config --cflags fuse)
2-
LDLIBS = -lcurl $(shell pkg-config --libs fuse)
1+
export PYTHONPATH=$(CURDIR)
2+
PYTEST?=pytest
33

4-
all: codegra-fs
4+
.PHONY: install_deps
5+
install_deps:
6+
pip install -r requirements.txt
7+
8+
.PHONY: format
9+
format:
10+
yapf -rip *.py
11+
12+
.PHONY: test
13+
test:
14+
coverage erase
15+
$(PYTEST) test/ -vvvvvvvvv
16+
coverage report -m cgfs.py

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,33 @@
11
# CodeGra.fs
22
CodeGra.de filesystem
3+
4+
<p align="center">
5+
<a href="https://travis-ci.org/CodeGra-de/CodeGra.fs">
6+
<img src="https://travis-ci.org/CodeGra-de/CodeGra.fs.svg?branch=master"
7+
alt="Build Status">
8+
</a>
9+
<a href="https://codegra.de">
10+
<img src="https://img.shields.io/badge/style-%E2%9D%A4%EF%B8%8F%20&%20%F0%9F%8D%BB-ff69b4.svg?label=made%20with"
11+
alt="Made with ❤ & ️🍻">
12+
</a>
13+
<a href="https://codegra.de">
14+
<img src="https://img.shields.io/badge/style-CodeGra.de-blue.svg?label=website"
15+
alt="CodeGra.de">
16+
</a>
17+
<a href="https://semver.org">
18+
<img src="https://img.shields.io/badge/semVer-v0.1.0--alpha-green.svg"
19+
alt="Semantic Version v0.1.0-alpha">
20+
</a>
21+
<a href="https://www.gnu.org/licenses/agpl-3.0.html">
22+
<img src="https://img.shields.io/badge/license-AGPL--3.0-blue.svg"
23+
alt="License AGPL-3.0">
24+
</a>
25+
<a href="https://matrix.to/#/#CodeGra.de:matrix.org">
26+
<img src="https://img.shields.io/badge/matrix-user-43ad8d.svg"
27+
alt="Chat on Matrix: #CodeGra.de:matrix.org">
28+
</a>
29+
<a href="https://matrix.to/#/#DevCodeGra.de:matrix.org">
30+
<img src="https://img.shields.io/badge/matrix-dev-4e42aa.svg"
31+
alt="Chat as developer on Matrix: #DevCodeGra.de:matrix.org">
32+
</a>
33+
</p>

cgapi.py

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from os import getenv
4+
from enum import IntEnum
5+
6+
import requests
7+
8+
CGAPI_BASE_URL = getenv('CGAPI_BASE_URL', 'https://codegra.de/api/v1')
9+
10+
11+
class APIRoutes():
12+
LOGIN = CGAPI_BASE_URL + '/login'
13+
COURSES = CGAPI_BASE_URL + '/courses/?extended=true'
14+
SUBMISSIONS = CGAPI_BASE_URL + '/assignments/{assignment_id}/submissions/'
15+
FILES = CGAPI_BASE_URL + '/submissions/{submission_id}/files/?owner=auto'
16+
FILE = CGAPI_BASE_URL + '/submissions/{submission_id}/files/?path={path}&owner=auto'
17+
FILE_BUF = CGAPI_BASE_URL + '/code/{file_id}'
18+
FILE_RENAME = CGAPI_BASE_URL + '/code/{file_id}?operation=rename&new_path={new_path}'
19+
20+
21+
class APICodes(IntEnum):
22+
"""Internal API codes that are used by :class:`APIException` objects.
23+
"""
24+
INCORRECT_PERMISSION = 0
25+
NOT_LOGGED_IN = 1
26+
OBJECT_ID_NOT_FOUND = 2
27+
OBJECT_WRONG_TYPE = 3
28+
MISSING_REQUIRED_PARAM = 4
29+
INVALID_PARAM = 5
30+
REQUEST_TOO_LARGE = 6
31+
LOGIN_FAILURE = 7
32+
INACTIVE_USER = 8
33+
INVALID_URL = 9
34+
OBJECT_NOT_FOUND = 10
35+
BLOCKED_ASSIGNMENT = 11
36+
INVALID_CREDENTIALS = 12
37+
INVALID_STATE = 13
38+
INVALID_OAUTH_REQUEST = 14
39+
DISABLED_FEATURE = 15
40+
41+
42+
class CGAPIException(Exception):
43+
def __init__(self, response):
44+
data = response.json()
45+
super(CGAPIException, self).__init__(data['message'])
46+
47+
self.description = data['description']
48+
self.message = data['message']
49+
self.code = data['code']
50+
51+
52+
class CGAPI():
53+
def __init__(self, username, password):
54+
r = requests.post(
55+
APIRoutes.LOGIN,
56+
json={
57+
'username': username,
58+
'password': password,
59+
}
60+
)
61+
62+
if r.status_code >= 400:
63+
raise CGAPIException(r)
64+
65+
self.access_token = r.json()['access_token']
66+
67+
def get_default_headers(self):
68+
return {
69+
'Authorization': 'Bearer ' + self.access_token,
70+
}
71+
72+
def get_courses(self):
73+
r = requests.get(APIRoutes.COURSES, headers=self.get_default_headers())
74+
75+
if r.status_code >= 400:
76+
raise CGAPIException(r)
77+
78+
return r.json()
79+
80+
def get_submissions(self, assignment_id):
81+
url = APIRoutes.SUBMISSIONS.format(assignment_id=assignment_id)
82+
r = requests.get(url, headers=self.get_default_headers())
83+
84+
if r.status_code >= 400:
85+
raise CGAPIException(r)
86+
87+
return r.json()
88+
89+
def get_submission_files(self, submission_id):
90+
url = APIRoutes.FILES.format(submission_id=submission_id)
91+
r = requests.get(url, headers=self.get_default_headers())
92+
93+
if r.status_code >= 400:
94+
raise CGAPIException(r)
95+
96+
return r.json()
97+
98+
def get_file_meta(self, submission_id, path):
99+
url = APIRoutes.FILE.format(submission_id=submission_id, path=path)
100+
r = requests.get(url, headers=self.get_default_headers())
101+
102+
if r.status_code >= 400:
103+
raise CGAPIException(r)
104+
105+
return r.json()
106+
107+
def create_file(self, submission_id, path, buf=None):
108+
url = APIRoutes.FILE.format(submission_id=submission_id, path=path)
109+
r = requests.post(url, headers=self.get_default_headers(), data=buf)
110+
111+
if r.status_code >= 400:
112+
raise CGAPIException(r)
113+
114+
return r.json()
115+
116+
def rename_file(self, file_id, new_path):
117+
url = APIRoutes.FILE_RENAME.format(file_id=file_id, new_path=new_path)
118+
r = requests.patch(url, headers=self.get_default_headers())
119+
120+
if r.status_code >= 400:
121+
raise CGAPIException(r)
122+
123+
return r.json()
124+
125+
def get_file(self, file_id):
126+
url = APIRoutes.FILE_BUF.format(file_id=file_id)
127+
r = requests.get(url, headers=self.get_default_headers())
128+
129+
if r.status_code >= 400:
130+
raise CGAPIException(r)
131+
132+
return r.content
133+
134+
def patch_file(self, file_id, buf):
135+
url = APIRoutes.FILE_BUF.format(file_id=file_id)
136+
r = requests.patch(url, headers=self.get_default_headers(), data=buf)
137+
138+
if r.status_code >= 400:
139+
raise CGAPIException(r)
140+
141+
return r.json()
142+
143+
def delete_file(self, file_id):
144+
url = APIRoutes.FILE_BUF.format(file_id=file_id)
145+
r = requests.delete(url, headers=self.get_default_headers())
146+
147+
if r.status_code >= 400:
148+
raise CGAPIException(r)

0 commit comments

Comments
 (0)